<!DOCTYPE html>
<html lang="en-US" dir="ltr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>vue3+setup+ts 学习笔记 | VitePress</title>
    <meta name="description" content="A VitePress site">
    <link rel="preload stylesheet" href="/notebook/assets/style.3dbfd0c2.css" as="style">
    
    <script type="module" src="/notebook/assets/app.8aaa4cbe.js"></script>
    <link rel="preload" href="/notebook/assets/inter-roman-latin.2ed14f66.woff2" as="font" type="font/woff2" crossorigin="">
    <link rel="modulepreload" href="/notebook/assets/chunks/framework.1336c4e5.js">
    <link rel="modulepreload" href="/notebook/assets/chunks/theme.20cddc0c.js">
    <link rel="modulepreload" href="/notebook/assets/1、学前端_3、Vue_Vue3_Vue3新语法.md.8afd5409.lean.js">
    <script id="check-dark-light">(()=>{const e=localStorage.getItem("vitepress-theme-appearance")||"",a=window.matchMedia("(prefers-color-scheme: dark)").matches;(!e||e==="auto"?a:e==="dark")&&document.documentElement.classList.add("dark")})();</script>
  </head>
  <body>
    <div id="app"><div class="Layout" data-v-255ec12d><!--[--><!--]--><!--[--><span tabindex="-1" data-v-ae3e3f51></span><a href="#VPContent" class="VPSkipLink visually-hidden" data-v-ae3e3f51> Skip to content </a><!--]--><!----><header class="VPNav" data-v-255ec12d data-v-7e5bc4a5><div class="VPNavBar has-sidebar" data-v-7e5bc4a5 data-v-0937f67c><div class="container" data-v-0937f67c><div class="title" data-v-0937f67c><div class="VPNavBarTitle has-sidebar" data-v-0937f67c data-v-86d1bed8><a class="title" href="/notebook/" data-v-86d1bed8><!--[--><!--]--><!--[--><img class="VPImage logo" src="/notebook/Vue.png" alt data-v-8426fc1a><!--]--><!--[-->任硕的文档<!--]--><!--[--><!--]--></a></div></div><div class="content" data-v-0937f67c><div class="curtain" data-v-0937f67c></div><div class="content-body" data-v-0937f67c><!--[--><!--]--><div class="VPNavBarSearch search" style="--vp-meta-key:&#39;Meta&#39;;" data-v-0937f67c><!--[--><!----><div id="docsearch"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg class="DocSearch-Search-Icon" width="20" height="20" viewBox="0 0 20 20" aria-label="search icon"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"><kbd class="DocSearch-Button-Key"></kbd><kbd class="DocSearch-Button-Key">K</kbd></span></button></div><!--]--></div><nav aria-labelledby="main-nav-aria-label" class="VPNavBarMenu menu" data-v-0937f67c data-v-7f418b0f><span id="main-nav-aria-label" class="visually-hidden" data-v-7f418b0f>Main Navigation</span><!--[--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>Java学前端</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/HTML+JS.html" data-v-2f2cfafc><!--[-->HTML+JS<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/CSS.html" data-v-2f2cfafc><!--[-->CSS<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/Vue2+%E7%BB%84%E4%BB%B6.html" data-v-2f2cfafc><!--[-->Vue2+组件<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/Vue3+%E7%BB%84%E4%BB%B6.html" data-v-2f2cfafc><!--[-->Vue3+组件<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/Java%E5%AD%A6%E5%89%8D%E7%AB%AF/React.html" data-v-2f2cfafc><!--[-->React<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>软件测试</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/%E6%B5%8B%E8%AF%95%E5%9F%BA%E7%A1%80.html" data-v-2f2cfafc><!--[-->测试基础<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E8%BD%AF%E4%BB%B6%E6%B5%8B%E8%AF%95/%E5%8E%8B%E5%8A%9B%E6%B5%8B%E8%AF%95.html" data-v-2f2cfafc><!--[-->压力测试<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>多线程</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E5%B9%B6%E5%8F%91%20&amp;%20%E5%A4%9A%E7%BA%BF%E7%A8%8B/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-2f2cfafc><!--[-->基础篇<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E5%B9%B6%E5%8F%91%20&amp;%20%E5%A4%9A%E7%BA%BF%E7%A8%8B/%E5%B9%B6%E5%8F%91%E5%AE%8C%E5%96%84.html" data-v-2f2cfafc><!--[-->进阶篇<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>开发工具</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/Chrome.html" data-v-2f2cfafc><!--[-->Chrome<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/IDEA%E5%9F%BA%E7%A1%80.html" data-v-2f2cfafc><!--[-->IDEA基础<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/IDEA%E6%8F%92%E4%BB%B6.html" data-v-2f2cfafc><!--[-->IDEA插件<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/IDEA/VS%20Code.html" data-v-2f2cfafc><!--[-->VS Code<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--[--><div class="VPFlyout VPNavBarMenuGroup" data-v-7f418b0f data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" data-v-a7b5672a><span class="text" data-v-a7b5672a><!----><span data-v-a7b5672a>消息中间件</span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="text-icon" data-v-a7b5672a><path d="M12,16c-0.3,0-0.5-0.1-0.7-0.3l-6-6c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l5.3,5.3l5.3-5.3c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-6,6C12.5,15.9,12.3,16,12,16z"></path></svg></span></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><div class="items" data-v-e7ea1737><!--[--><!--[--><div class="VPMenuGroup" data-v-e7ea1737 data-v-69e747b5><!----><!--[--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/RabbitMQ.html" data-v-2f2cfafc><!--[-->RabbitMQ<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/RocketMQ.html" data-v-2f2cfafc><!--[-->RocketMQ<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/Kafka.html" data-v-2f2cfafc><!--[-->Kafka<!--]--></a></div><!--]--><!--[--><div class="VPMenuLink" data-v-69e747b5 data-v-2f2cfafc><a class="VPLink link" href="/notebook/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6/Canal.html" data-v-2f2cfafc><!--[-->Canal<!--]--></a></div><!--]--><!--]--></div><!--]--><!--]--></div><!--[--><!--]--></div></div></div><!--]--><!--]--></nav><!----><div class="VPNavBarAppearance appearance" data-v-0937f67c data-v-f6a63727><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-f6a63727 data-v-82b282f1 data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-82b282f1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-82b282f1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div><div class="VPSocialLinks VPNavBarSocialLinks social-links" data-v-0937f67c data-v-0394ad82 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/renshuo123/renshuo123.github.io" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="#" aria-label="twitter" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a><a class="VPSocialLink no-icon" href="https://github.com/" aria-label target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg t="1676028692954" class="icon" ...</path></svg></a><!--]--></div><div class="VPFlyout VPNavBarExtra extra" data-v-0937f67c data-v-40855f84 data-v-a7b5672a><button type="button" class="button" aria-haspopup="true" aria-expanded="false" aria-label="extra navigation" data-v-a7b5672a><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="icon" data-v-a7b5672a><circle cx="12" cy="12" r="2"></circle><circle cx="19" cy="12" r="2"></circle><circle cx="5" cy="12" r="2"></circle></svg></button><div class="menu" data-v-a7b5672a><div class="VPMenu" data-v-a7b5672a data-v-e7ea1737><!----><!--[--><!--[--><!----><div class="group" data-v-40855f84><div class="item appearance" data-v-40855f84><p class="label" data-v-40855f84>Appearance</p><div class="appearance-action" data-v-40855f84><button class="VPSwitch VPSwitchAppearance" type="button" role="switch" title="toggle dark mode" aria-checked="false" data-v-40855f84 data-v-82b282f1 data-v-f3c41672><span class="check" data-v-f3c41672><span class="icon" data-v-f3c41672><!--[--><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="sun" data-v-82b282f1><path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z"></path><path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z"></path><path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z"></path><path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z"></path><path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z"></path><path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z"></path><path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z"></path><path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z"></path><path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="moon" data-v-82b282f1><path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z"></path></svg><!--]--></span></span></button></div></div></div><div class="group" data-v-40855f84><div class="item social-links" data-v-40855f84><div class="VPSocialLinks social-links-list" data-v-40855f84 data-v-7bc22406><!--[--><a class="VPSocialLink no-icon" href="https://github.com/renshuo123/renshuo123.github.io" aria-label="github" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg></a><a class="VPSocialLink no-icon" href="#" aria-label="twitter" target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Twitter</title><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg></a><a class="VPSocialLink no-icon" href="https://github.com/" aria-label target="_blank" rel="noopener" data-v-7bc22406 data-v-f80f8133><svg t="1676028692954" class="icon" ...</path></svg></a><!--]--></div></div></div><!--]--><!--]--></div></div></div><!--[--><!--]--><button type="button" class="VPNavBarHamburger hamburger" aria-label="mobile navigation" aria-expanded="false" aria-controls="VPNavScreen" data-v-0937f67c data-v-e5dd9c1c><span class="container" data-v-e5dd9c1c><span class="top" data-v-e5dd9c1c></span><span class="middle" data-v-e5dd9c1c></span><span class="bottom" data-v-e5dd9c1c></span></span></button></div></div></div></div><!----></header><div class="VPLocalNav reached-top" data-v-255ec12d data-v-5cfd5582><button class="menu" aria-expanded="false" aria-controls="VPSidebarNav" data-v-5cfd5582><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="menu-icon" data-v-5cfd5582><path d="M17,11H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,11,17,11z"></path><path d="M21,7H3C2.4,7,2,6.6,2,6s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,7,21,7z"></path><path d="M21,15H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h18c0.6,0,1,0.4,1,1S21.6,15,21,15z"></path><path d="M17,19H3c-0.6,0-1-0.4-1-1s0.4-1,1-1h14c0.6,0,1,0.4,1,1S17.6,19,17,19z"></path></svg><span class="menu-text" data-v-5cfd5582>Menu</span></button><div class="VPLocalNavOutlineDropdown" style="--vp-vh:0px;" data-v-5cfd5582 data-v-18201f51><button data-v-18201f51>Return to top</button><!----></div></div><aside class="VPSidebar" data-v-255ec12d data-v-845b8fc6><div class="curtain" data-v-845b8fc6></div><nav class="nav" id="VPSidebarNav" aria-labelledby="sidebar-aria-label" tabindex="-1" data-v-845b8fc6><span class="visually-hidden" id="sidebar-aria-label" data-v-845b8fc6> Sidebar Navigation </span><!--[--><!--]--><!--[--><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Java</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E6%96%B0%E7%89%B9%E6%80%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java新特性</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E9%9B%86%E5%90%88.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java集合</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Java/Java%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Java高级</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Linux</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/Linux%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Linux基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/Linux%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Linux新特性</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/Shell.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Shell脚本</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/%E5%AE%9E%E7%94%A8%E8%84%9A%E6%9C%AC.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>实用脚本</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Linux/%E8%BD%AF%E4%BB%B6%E9%83%A8%E7%BD%B2.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>软件部署</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Nginx</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E5%AE%9E%E6%88%98%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>实战篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Nginx/%E9%9D%A2%E8%AF%95%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>面试篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SSM</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/Maven.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Maven</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/Spring.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Spring</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/SpringMVC.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringMVC</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/SSM/SpringBatch.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringBatch</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SpringBoot</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E5%BA%94%E7%94%A8%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>应用篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E6%96%B0%E7%89%B9%E6%80%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>新特性</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/3%E3%80%81SpringBoot/%E8%BF%90%E7%BB%B4&amp;%E5%8E%9F%E7%90%86.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>运维&原理</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SpringCloud</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringCloud</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/%E5%BF%85%E5%A4%87/Sentinel.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Sentinel</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>SpringSecurity</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/SpringSecurity/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringSecurity基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/SpringSecurity/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringSecurity进阶篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/SpringSecurity/%E9%AB%98%E7%BA%A7%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>SpringSecurity高级篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Mybatis & MybatisPlus</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Mybatis&amp;MybatisPlus/Mybatis.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Mybatis</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Mybatis&amp;MybatisPlus/MybatisPlus.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MybatisPlus</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/Mybatis&amp;MybatisPlus/JPA.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>JPA</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>Git & ChatGPT</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Git.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Git</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Github.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Github</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/ChatGPT.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ChatGPT</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Jenkins.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Jenkins</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/5%E3%80%81%E8%BF%90%E7%BB%B4/Netty.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Netty</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>数据库</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>MySQL</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E4%BC%98%E5%8C%96.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL优化</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E8%AE%BE%E8%AE%A1.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL设计</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/MySQL%E6%A0%B8%E5%BF%83/%E8%BF%90%E7%BB%B4.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MySQL运维</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MySQL/%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>分库分表</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>Redis</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E4%BC%98%E5%8C%96.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis优化</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E5%8E%9F%E7%90%86.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis原理</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis高级</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/Redis%E5%AE%9E%E6%88%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Redis实战</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Redis/%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>本地缓存</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>MongoDB</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MongoDB/%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MongoDB基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/MongoDB/%E6%95%B4%E5%90%88.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>MongoDB进阶</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>ElasticSearch</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/ElasticSearch/1%E3%80%81ES%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/ElasticSearch/3%E3%80%81ES%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES高级</p><!--]--></a><!----></div><!----></div><!--]--></div></section><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/influxdb.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>InfluxDB</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/2%E3%80%81%E6%95%B0%E6%8D%AE%E5%BA%93/Neo4j.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Neo4j</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>高并发 & 秒杀 & 分布式</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E5%88%86%E5%B8%83%E5%BC%8F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>分布式理论</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/4%E3%80%81%E5%BE%AE%E6%9C%8D%E5%8A%A1/%E5%BF%85%E5%A4%87/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>分布式锁</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E7%A7%92%E6%9D%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>秒杀</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E9%AB%98%E5%8F%AF%E7%94%A8.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>高可用</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%B8%89%E9%AB%98/%E9%AB%98%E5%B9%B6%E5%8F%91.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>高并发</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>云原生</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%BA%91%E5%8E%9F%E7%94%9F/Docker.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Docker</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E4%BA%91%E5%8E%9F%E7%94%9F/K8S.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>K8S</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>可视化 & 监控</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/%E7%9B%91%E6%8E%A7%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>监控基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/%E7%9B%91%E6%8E%A7%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>监控进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/%E5%8F%AF%E8%A7%86%E5%8C%96%E5%A4%A7%E5%B1%8F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>可视化大屏</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E5%8F%AF%E8%A7%86%E5%8C%96%20&amp;%20%E7%9B%91%E6%8E%A7/Zabbix.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Zabbix</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible has-active" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>学前端</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>HTML+CSS</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/1%E3%80%81HTML+CSS/HTML%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>HTML基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/1%E3%80%81HTML+CSS/CSS%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>CSS基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/1%E3%80%81HTML+CSS/%E7%BD%91%E9%A1%B5%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>网页进阶</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>JS+TS</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/JS%20%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>JS基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/JS%20%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>JS进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/ES6%20%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES6基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/ES6%20%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>ES6进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/2%E3%80%81JS+TS/TypeScript.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>TS基础</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>NodeJS</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/4%E3%80%81Node/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Node基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/4%E3%80%81Node/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Node进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/4%E3%80%81Node/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目实战</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible has-active" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>Vue</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue3/Vue3%E8%BF%9B%E9%98%B6.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Vue3进阶</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue3/Vue3%E9%AB%98%E7%BA%A7.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Vue3高级</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue3/Vue3%E6%96%B0%E8%AF%AD%E6%B3%95.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>Vue3新语法</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue2/Vue2%E9%A1%B9%E7%9B%AE.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目实战</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>小程序</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>小程序基础</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E4%BC%98%E5%8C%96.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>小程序优化</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/uniapp.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>uniapp</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/5%E3%80%81%E5%B0%8F%E7%A8%8B%E5%BA%8F/%E5%B0%8F%E7%A8%8B%E5%BA%8F%E9%A1%B9%E7%9B%AE.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目实战</p><!--]--></a><!----></div><!----></div><!--]--></div></section><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>计算机基础</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>数据结构</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>操作系统</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>设计模式</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>计算机网络</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/UML.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>UML</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/%E7%AE%97%E6%B3%95/LeetCode.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>LeetCode</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0 collapsible collapsed" data-v-845b8fc6 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h2 class="text" data-v-9b797284>项目实战</h2><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>云尚办公</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E4%BA%91%E5%B0%9A%E5%8A%9E%E5%85%AC/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>小兔鲜</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E5%B0%8F%E5%85%94%E9%B2%9C/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E5%B0%8F%E5%85%94%E9%B2%9C/%E8%BF%9B%E9%98%B6%E7%AF%871.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇1</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E5%B0%8F%E5%85%94%E9%B2%9C/%E8%BF%9B%E9%98%B6%E7%AF%872.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇2</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>地图</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>苍穹外卖</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><section class="VPSidebarItem level-1 collapsible collapsed" data-v-9b797284 data-v-9b797284><div class="item" role="button" tabindex="0" data-v-9b797284><div class="indicator" data-v-9b797284></div><h3 class="text" data-v-9b797284>黑马头条</h3><div class="caret" role="button" aria-label="toggle section" tabindex="0" data-v-9b797284><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewbox="0 0 24 24" class="caret-icon" data-v-9b797284><path d="M9,19c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l5.3-5.3L8.3,6.7c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l6,6c0.4,0.4,0.4,1,0,1.4l-6,6C9.5,18.9,9.3,19,9,19z"></path></svg></div></div><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E5%9F%BA%E7%A1%80%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>基础篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E8%BF%9B%E9%98%B6%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E8%BF%9B%E9%98%B6%E7%AF%872.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>进阶篇2</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-2 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%BB%91%E9%A9%AC%E5%A4%B4%E6%9D%A1/%E9%AB%98%E7%BA%A7%E7%AF%87.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>高级篇</p><!--]--></a><!----></div><!----></div><!--]--></div></section><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E6%94%AF%E4%BB%98.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>支付</p><!--]--></a><!----></div><!----></div><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98/%E9%A1%B9%E7%9B%AE%E6%8E%A8%E8%8D%90.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>项目推荐</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><div class="group" data-v-845b8fc6><section class="VPSidebarItem level-0" data-v-845b8fc6 data-v-9b797284><!----><div class="items" data-v-9b797284><!--[--><div class="VPSidebarItem level-1 is-link" data-v-9b797284 data-v-9b797284><div class="item" data-v-9b797284><div class="indicator" data-v-9b797284></div><a class="VPLink link link" href="/notebook/team.html" data-v-9b797284><!--[--><p class="text" data-v-9b797284>团队成员</p><!--]--></a><!----></div><!----></div><!--]--></div></section></div><!--]--><!--[--><!--]--></nav></aside><div class="VPContent has-sidebar" id="VPContent" data-v-255ec12d data-v-669faec9><div class="VPDoc has-sidebar has-aside" data-v-669faec9 data-v-6b87e69f><!--[--><!--]--><div class="container" data-v-6b87e69f><div class="aside" data-v-6b87e69f><div class="aside-curtain" data-v-6b87e69f></div><div class="aside-container" data-v-6b87e69f><div class="aside-content" data-v-6b87e69f><div class="VPDocAside" data-v-6b87e69f data-v-3f215769><!--[--><!--]--><!--[--><!--]--><div class="VPDocAsideOutline" data-v-3f215769 data-v-ff0f39c8><div class="content" data-v-ff0f39c8><div class="outline-marker" data-v-ff0f39c8></div><div class="outline-title" data-v-ff0f39c8>On this page</div><nav aria-labelledby="doc-outline-aria-label" data-v-ff0f39c8><span class="visually-hidden" id="doc-outline-aria-label" data-v-ff0f39c8> Table of Contents for current page </span><ul class="root" data-v-ff0f39c8 data-v-d0ee3533><!--[--><!--]--></ul></nav></div></div><!--[--><!--]--><div class="spacer" data-v-3f215769></div><!--[--><!--]--><!----><!--[--><!--]--><!--[--><!--]--></div></div></div></div><div class="content" data-v-6b87e69f><div class="content-container" data-v-6b87e69f><!--[--><!--]--><!----><main class="main" data-v-6b87e69f><div style="position:relative;" class="vp-doc _notebook_1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF_3%E3%80%81Vue_Vue3_Vue3%E6%96%B0%E8%AF%AD%E6%B3%95" data-v-6b87e69f><div><h1 id="vue3-setup-ts-学习笔记" tabindex="-1">vue3+setup+ts 学习笔记 <a class="header-anchor" href="#vue3-setup-ts-学习笔记" aria-label="Permalink to &quot;vue3+setup+ts 学习笔记&quot;">​</a></h1><p>在 vue3.2 中，我们只需在script标签中添加setup。就可以做到，组件只需引入不用注册，属性和方法也不用 return 才能于 template 中使用，也不用写setup函数，也不用写export default ，甚至是自定义指令也可以在我们的template中自动获得。本次我们的学习也将在 setup 语法糖下进行。</p><h2 id="环境搭建" tabindex="-1">环境搭建 <a class="header-anchor" href="#环境搭建" aria-label="Permalink to &quot;环境搭建&quot;">​</a></h2><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm init vue@latest</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.3.30/202303101611590.png" alt="image-20230310161153527" style="zoom:67%;"><h2 id="ref-和-reactive" tabindex="-1">ref 和 reactive <a class="header-anchor" href="#ref-和-reactive" aria-label="Permalink to &quot;ref 和 reactive&quot;">​</a></h2><ul><li>ref: 用来给基本数据类型绑定响应式数据，访问时需要通过 .value 的形式， tamplate 会自动解析,不需要 .value</li><li>reactive: 用来给 复杂数据类型 绑定响应式数据，直接访问即可</li></ul><blockquote><p>ref其实也是内部调用 reactive 来实现的</p></blockquote><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;{{title}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;h4&gt;{{userInfo}}&lt;/h4&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, reactive } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;">type Person = {</span></span>
<span class="line"><span style="color:#A6ACCD;">    name: string;</span></span>
<span class="line"><span style="color:#A6ACCD;">    age: number;</span></span>
<span class="line"><span style="color:#A6ACCD;">    gender?: string;</span></span>
<span class="line"><span style="color:#A6ACCD;">};</span></span>
<span class="line"><span style="color:#A6ACCD;">const title = ref&lt;string&gt;(&quot;彼时彼刻，恰如此时此刻&quot;);</span></span>
<span class="line"><span style="color:#A6ACCD;">const userInfo = reactive&lt;Person&gt;({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;树哥&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="toref、torefs、toraw" tabindex="-1">toRef、toRefs、toRaw <a class="header-anchor" href="#toref、torefs、toraw" aria-label="Permalink to &quot;toRef、toRefs、toRaw&quot;">​</a></h2><h3 id="toref" tabindex="-1">toRef <a class="header-anchor" href="#toref" aria-label="Permalink to &quot;toRef&quot;">​</a></h3><p>toRef 如果原始对象是非响应式的,数据会变,但不会更新视图</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">     &lt;button @click=&quot;change&quot;&gt;按钮&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">     {{state}}</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { reactive, toRef } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const obj = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;树哥&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const state = toRef(obj, &#39;age&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const change = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  state.value++</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(&#39;obj:&#39;,obj,&#39;state:&#39;, state);</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ic5A4V8PX4PniaElFR8nqkPVh6iajiaF7ytv3dxLqlB3Yp5ricwUEny8GUFWgpMyXtHP5PkgDmzzRF2icwQphxZyFBrQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>可以看到，点击按钮，当原始对象是非响应式时，使用toRef 的数据改变，但是试图并没有更新</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;button @click=&quot;change&quot;&gt;按钮&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    {{state}}</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { reactive, toRef } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const obj = reactive({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;树哥&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const state = toRef(obj, &#39;age&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const change = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  state.value++</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(&#39;obj:&#39;, obj, &#39;state:&#39;, state);</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ic5A4V8PX4PniaElFR8nqkPVh6iajiaF7ytvdkzRaBSRNV59pWgbh1OncKyCwcgkA3omYcFM7hibtbK70jeeD41PkIA/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>当我们把 obj 用 reactive 包裹，再使用 toRef，点击按钮时，可以看到视图和数据都变了</p><blockquote><p>toRef返回的值是否具有响应性取决于被解构的对象本身是否具有响应性。响应式数据经过toRef返回的值仍具有响应性，非响应式数据经过toRef返回的值仍没有响应性。</p></blockquote><h3 id="torefs" tabindex="-1">toRefs <a class="header-anchor" href="#torefs" aria-label="Permalink to &quot;toRefs&quot;">​</a></h3><p>toRefs相当于对对象内每个属性调用toRef，toRefs返回的对象内的属性使用时需要加.value,主要是方便我们解构使用</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;button @click=&quot;change&quot;&gt;按钮&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    name--{{name}}---age{{age}}</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { reactive, toRefs } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const obj = reactive({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;树哥&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let { name, age } = toRefs(obj)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const change = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  age.value++</span></span>
<span class="line"><span style="color:#A6ACCD;">  name.value = &#39;张麻子&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(&#39;obj:&#39;, obj);</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(&#39;name:&#39;, name);</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(&#39;age:&#39;, age);</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>简单理解就是批量版的toRef,(<strong>其源码实现也正是通过对象循环调用了toRef</strong>)</p><h3 id="toraw" tabindex="-1">toRaw <a class="header-anchor" href="#toraw" aria-label="Permalink to &quot;toRaw&quot;">​</a></h3><p>将响应式对象修改为普通对象</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;button @click=&quot;change&quot;&gt;按钮&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    {{data}}</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { reactive, toRaw } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const obj = reactive({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;树哥&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const data = toRaw(obj)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const change = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  data.age = 19</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(&#39;obj:&#39;, obj, &#39;data:&#39;, data);</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ic5A4V8PX4PniaElFR8nqkPVh6iajiaF7ytvL5N5w4e8fZLRiaYIibSYiaH2jCMKVrWZJImIv1VRPAQlbtn84HJyvwTYw/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>数据能变化，视图不变化(失去响应式)</p><h2 id="computed" tabindex="-1">computed <a class="header-anchor" href="#computed" aria-label="Permalink to &quot;computed&quot;">​</a></h2><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;{{title}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;h4&gt;{{userInfo}}&lt;/h4&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;h1&gt;{{add}}&lt;/h1&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, reactive,computed } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;">const count = ref(0)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">// 推导得到的类型：ComputedRef&lt;number&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">const add = computed(() =&gt; count.value +1)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="watch" tabindex="-1">watch <a class="header-anchor" href="#watch" aria-label="Permalink to &quot;watch&quot;">​</a></h2><p>vue3 watch 的作用和 Vue2 中的 watch 作用是一样的，他们都是用来监听响应式状态发生变化的，当响应式状态发生变化时，就会触发一个回调函数。</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">watch(data,()=&gt;{},{})</span></span></code></pre></div><ul><li>参数一，监听的数据</li><li>参数二，数据改变时触发的回调函数（newVal,oldVal）</li><li>参数三，options配置项，为一个对象</li></ul><p><strong>1、监听ref定义的一个响应式数据</strong></p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, watch } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const str = ref(&#39;彼时彼刻&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变str的值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; { str.value = &#39;恰如此时此刻&#39; }, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">watch(str, (newV, oldV) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(newV, oldV) //恰如此时此刻 彼时彼刻</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p><strong>2、监听多个ref</strong></p><p><strong>这时候写法变为数组的形式</strong></p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, watch } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let name = ref(&#39;树哥&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">let age = ref(18)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  name.value = &#39;我叫树哥&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">  age.value = 19</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">watch([name, age], (newV, oldV) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(newV, oldV) // [&#39;我叫树哥&#39;, 19]  [&#39;树哥&#39;, 18]</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p><strong>3、监听Reactive定义的响应式对象</strong></p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { reactive, watch } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let info = reactive({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;树哥&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  info.age = 19</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">watch(info, (newV, oldV) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(newV, oldV) </span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>当 watch 监听的是一个响应式对象时，会隐式地创建一个深层侦听器，即该响应式对象里面的任何属性发生变化，都会触发监听函数中的回调函数。<strong>即当 watch 监听的是一个响应式对象时，默认开启 deep：true</strong></p><p><strong>4、监听reactive 定义响应式对象的单一属性</strong></p><p>错误写法</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { reactive, watch } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let info = reactive({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;树哥&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  info.age = 19</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">watch(info.age, (newV, oldV) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(newV, oldV) </span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>可以看到控制台出现警告</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">[Vue warn]: Invalid watch source:  18 A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types. </span></span>
<span class="line"><span style="color:#A6ACCD;">  at &lt;Index&gt; </span></span>
<span class="line"><span style="color:#A6ACCD;">  at &lt;App&gt;</span></span></code></pre></div><p>如果我们非要监听响应式对象中的某个属性，我们可以使用 getter 函数的形式,<strong>即将watch第一个参数修改成一个回调函数的形式</strong></p><p>正确写法：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">// 其他不变</span></span>
<span class="line"><span style="color:#A6ACCD;">watch(()=&gt;info.age, (newV, oldV) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(newV, oldV) // 19 18</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>5、监听reactive定义的 引用数据</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { reactive, watch } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let info = reactive({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name: &#39;张麻子&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  age: 18,</span></span>
<span class="line"><span style="color:#A6ACCD;">  obj: {</span></span>
<span class="line"><span style="color:#A6ACCD;">    str: &#39;彼时彼刻，恰如此时此刻&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变s值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  info.obj.str = &#39;to be or not to be&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">// 需要自己开启 deep:true深度监听,不然不发触发 watch 的回调函数</span></span>
<span class="line"><span style="color:#A6ACCD;">watch(() =&gt; info.obj, (newV, oldV) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(newV, oldV)</span></span>
<span class="line"><span style="color:#A6ACCD;">}, {</span></span>
<span class="line"><span style="color:#A6ACCD;">  deep: true</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="watcheffect" tabindex="-1">WatchEffect <a class="header-anchor" href="#watcheffect" aria-label="Permalink to &quot;WatchEffect&quot;">​</a></h2><p>会立即执行传入的一个函数，同时响应式追踪其依赖，并在其依赖变更时重新运行该函数。（有点像计算属性）</p><p>如果用到 a 就只会监听 a, 就是用到几个监听几个 而且是非惰性,会默认调用一次</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, watchEffect } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let num = ref(0)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  num.value++</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">watchEffect(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(&#39;num 值改变：&#39;, num.value)</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>可以在控制台上看到，第一次进入页面时，打印出<code>num 值改变：0</code>,三秒后，再次打印<code>num 值改变：1</code></p><ul><li><strong>停止监听</strong></li></ul><p>当 watchEffect 在组件的 setup() 函数或生命周期钩子被调用时，侦听器会被链接到该组件的生命周期，并在组件卸载时自动停止。</p><p>但是我们采用异步的方式创建了一个监听器，这个时候监听器没有与当前组件绑定，所以即使组件销毁了，监听器依然存在。</p><p>这个时候我们可以显式调用停止监听</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { watchEffect } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">// 它会自动停止</span></span>
<span class="line"><span style="color:#A6ACCD;">watchEffect(() =&gt; {})</span></span>
<span class="line"><span style="color:#A6ACCD;">// ...这个则不会！</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  watchEffect(() =&gt; {})</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 100)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const stop = watchEffect(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  /* ... */</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">// 显式调用</span></span>
<span class="line"><span style="color:#A6ACCD;">stop()</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p><strong>清除副作用（onInvalidate）</strong></p><p>watchEffect 的第一个参数——effect函数——可以接收一个参数：叫onInvalidate，也是一个函数，用于清除 effect 产生的副作用</p><p>就是在触发监听之前会调用一个函数可以处理你的逻辑，例如防抖</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">import { ref, watchEffect } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let num = ref(0)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  num.value++</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">watchEffect((onInvalidate) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(num.value)</span></span>
<span class="line"><span style="color:#A6ACCD;">  onInvalidate(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">    console.log(&#39;执行&#39;);</span></span>
<span class="line"><span style="color:#A6ACCD;">  });</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span></code></pre></div><p>控制台依次输出：0 =&gt; 执行 =&gt; 1</p><ul><li><strong>配置选项</strong></li></ul><p>watchEffect的第二个参数，用来定义副作用刷新时机，可以作为一个调试器来使用</p><p>flush （更新时机）：</p><ul><li>1、pre：组件更新前执行</li><li>2、sync：强制效果始终同步触发</li><li>3、post：组件更新后执行</li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, watchEffect } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">let num = ref(0)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">//3s后改变值</span></span>
<span class="line"><span style="color:#A6ACCD;">setTimeout(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  num.value++</span></span>
<span class="line"><span style="color:#A6ACCD;">}, 3000)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">watchEffect((onInvalidate) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(num.value)</span></span>
<span class="line"><span style="color:#A6ACCD;">  onInvalidate(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">    console.log(&#39;执行&#39;);</span></span>
<span class="line"><span style="color:#A6ACCD;">  });</span></span>
<span class="line"><span style="color:#A6ACCD;">}, {</span></span>
<span class="line"><span style="color:#A6ACCD;">  flush: &quot;post&quot;, //此时这个函数会在组件更新之后去执行</span></span>
<span class="line"><span style="color:#A6ACCD;">  onTrigger(e) { //作为一个调试工具，可在开发中方便调试</span></span>
<span class="line"><span style="color:#A6ACCD;">    console.log(&#39;触发&#39;, e);</span></span>
<span class="line"><span style="color:#A6ACCD;">  },</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="生命周期" tabindex="-1">生命周期 <a class="header-anchor" href="#生命周期" aria-label="Permalink to &quot;生命周期&quot;">​</a></h2><p>和 vue2 相比的话，基本上就是将 Vue2 中的beforeDestroy名称变更成beforeUnmount; destroyed 表更为 unmounted；然后用setup代替了两个钩子函数 beforeCreate 和 created；新增了两个开发环境用于调试的钩子</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2023.3.30/202303101546688.png" alt="image-20230310154644488" style="zoom:80%;"><h2 id="父子组件传参" tabindex="-1">父子组件传参 <a class="header-anchor" href="#父子组件传参" aria-label="Permalink to &quot;父子组件传参&quot;">​</a></h2><h3 id="defineprops" tabindex="-1">defineProps <a class="header-anchor" href="#defineprops" aria-label="Permalink to &quot;defineProps&quot;">​</a></h3><p>父组件传参</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Children :msg=&quot;msg&quot; :list=&quot;list&quot;&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, reactive } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import Children from &#39;./Children.vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const msg = ref(&#39;hello 啊，树哥&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">const list = reactive&lt;number[]&gt;([1, 2, 3])</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><blockquote><p>setup 中，引入的组件会自动注册，所以可以直接使用，无需再通过components进行注册</p></blockquote><p>子组件接受值</p><p>defineProps 来接收父组件传递的值， <strong>defineProps是无须引入的直接使用即可</strong></p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;msg：{{msg}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;list：{{list}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">defineProps&lt;{</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg: string,</span></span>
<span class="line"><span style="color:#A6ACCD;">  list: number[]</span></span>
<span class="line"><span style="color:#A6ACCD;">}&gt;()</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>使用 withDefaults 定义默认值</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;msg：{{msg}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;list：{{list}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">type Props = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg?: string,</span></span>
<span class="line"><span style="color:#A6ACCD;">  list?: number[]</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">// withDefaults 的第二个参数便是默认参数设置，会被编译为运行时 props 的 default 选项</span></span>
<span class="line"><span style="color:#A6ACCD;">withDefaults(defineProps&lt;Props&gt;(), {</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg: &#39;张麻子&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  list: () =&gt; [4, 5, 6]</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>子组件向父组件抛出事件</p><h3 id="defineemits" tabindex="-1">defineEmits <a class="header-anchor" href="#defineemits" aria-label="Permalink to &quot;defineEmits&quot;">​</a></h3><p>子组件派发事件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;msg：{{msg}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;list：{{list}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;button @click=&quot;onChangeMsg&quot;&gt;改变msg&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">type Props = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg?: string,</span></span>
<span class="line"><span style="color:#A6ACCD;">  list?: number[]</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">withDefaults(defineProps&lt;Props&gt;(), {</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg: &#39;张麻子&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  list: () =&gt; [4, 5, 6]</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const emits = defineEmits([&#39;changeMsg&#39;])</span></span>
<span class="line"><span style="color:#A6ACCD;">const onChangeMsg = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">emits(&#39;changeMsg&#39;,&#39;黄四郎&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>子组件绑定了一个click 事件 然后通过defineEmits 注册了一个自定义事件,点击按钮的时候，触发 emit 调用我们注册的事件，传递参数</p><p>父组件接收</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Children :msg=&quot;msg&quot; :list=&quot;list&quot; @changeMsg=&quot;changeMsg&quot;&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, reactive } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import Children from &#39;./Children.vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const msg = ref(&#39;hello 啊，树哥&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">const list = reactive&lt;number[]&gt;([1, 2, 3])</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const changeMsg = (v: string) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg.value = v</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h3 id="defineexpose-获取子组件的实例和内部属性" tabindex="-1">defineExpose 获取子组件的实例和内部属性 <a class="header-anchor" href="#defineexpose-获取子组件的实例和内部属性" aria-label="Permalink to &quot;defineExpose 获取子组件的实例和内部属性&quot;">​</a></h3><p>在 script-setup 模式下，所有数据只是默认 return 给 template 使用，不会暴露到组件外，所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。</p><p>如果要调用子组件的数据，需要先在子组件显示的暴露出来，才能够正确的拿到，这个操作，就是由 defineExpose 来完成。</p><p>子组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;p&gt;{{name}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script lang=&quot;ts&quot; setup&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const name = ref(&#39;张麻子&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">const changeName = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  name.value = &#39;县长&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">// 将方法、变量暴露给父组件使用，父组件才可通过 ref API拿到子组件暴露的数据</span></span>
<span class="line"><span style="color:#A6ACCD;">defineExpose({</span></span>
<span class="line"><span style="color:#A6ACCD;">  name,</span></span>
<span class="line"><span style="color:#A6ACCD;">  changeName</span></span>
<span class="line"><span style="color:#A6ACCD;">})</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>父组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;child ref=&#39;childRef&#39; /&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;button @click=&quot;getName&quot;&gt;获取子组件中的数据&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script lang=&quot;ts&quot; setup&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import child from &#39;./Child.vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">// 子组件ref（TypeScript语法）</span></span>
<span class="line"><span style="color:#A6ACCD;">const childRef = ref&lt;InstanceType&lt;typeof child&gt;&gt;()</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const getName = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 获取子组件name</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(childRef.value!.name)</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 执行子组件方法</span></span>
<span class="line"><span style="color:#A6ACCD;">  childRef.value?.changeName()</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 获取修改后的name</span></span>
<span class="line"><span style="color:#A6ACCD;">  console.log(childRef.value!.name)</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>注意：defineProps 、defineEmits 、 defineExpose 和 withDefaults 这四个宏函数只能在 <code>&lt;script setup&gt;</code> 中使用。他们不需要导入，会随着<code>&lt;script setup&gt;</code>的处理过程中一起被编译。</p><h2 id="插槽" tabindex="-1">插槽 <a class="header-anchor" href="#插槽" aria-label="Permalink to &quot;插槽&quot;">​</a></h2><p>在 Vue2 的中一般中具名插槽和作用域插槽分别使用slot和slot-scope来实现，如：</p><p>父组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p style=&quot;color:red&quot;&gt;父组件&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;Child ref=&#39;childRef&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;template slot=&quot;content&quot; slot-scope=&quot;{ msg }&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;div&gt;{{ msg }}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/Child&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script lang=&quot;ts&quot; setup&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import Child from &#39;./Child.vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>子组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;child&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;slot name=&quot;content&quot; msg=&quot;hello 啊，树哥!&quot;&gt;&lt;/slot&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span></code></pre></div><p>在 Vue3 中将slot和slot-scope进行了合并统一使用，使用 v-slot， <code>v-slot:slotName</code> 简写 <code>#slotName</code></p><p>父组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p style=&quot;color:red&quot;&gt;父组件&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;Child&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;template  v-slot:content=&quot;{ msg }&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;div&gt;{{ msg }}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/Child&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script lang=&quot;ts&quot; setup&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import Child from &#39;./Child.vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;!-- 简写 --&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;Child&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;template #content=&quot;{ msg }&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;div&gt;{{ msg }}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/Child&gt;</span></span></code></pre></div><p>实际上,v-slot 在 Vue2.6+ 的版本就可以使用。</p><h2 id="异步组件" tabindex="-1">异步组件 <a class="header-anchor" href="#异步组件" aria-label="Permalink to &quot;异步组件&quot;">​</a></h2><p>通过 defineAsyncComponent 异步加载</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Children :msg=&quot;msg&quot; :list=&quot;list&quot; @changeMsg=&quot;changeMsg&quot;&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, reactive,defineAsyncComponent } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">// import Children from &#39;./Children.vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">const Children = defineAsyncComponent(() =&gt; import(&#39;./Children.vue&#39;))</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="suspense" tabindex="-1">Suspense <a class="header-anchor" href="#suspense" aria-label="Permalink to &quot;Suspense&quot;">​</a></h2><p>Suspense 允许应用程序在等待异步组件时渲染一些其它内容,在 Vue2 中，必须使用条件判断(例如 v-if、 v-else等)来检查数据是否已加载并显示一些其它内容；但是，在 Vue3 新增了 Suspense 了，就不必跟踪何时加载数据并呈现相应的内容。</p><p>他是一个带插槽的组件，只是它的插槽指定了default 和 fallback 两种状态。</p><p>Suspense 使用：</p><ul><li>1、使用 <code>&lt;Suspense&gt;&lt;/Suspense&gt;</code> 包裹所有异步组件相关代码</li><li>2、<code>&lt;template v-slot:default&gt;&lt;/template&gt;</code> 插槽包裹异步组件</li><li>3、<code>&lt;template v-slot:fallback&gt;&lt;/template&gt;</code> 插槽包裹渲染异步组件渲染之前的内容</li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Suspense&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;template #default&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;!-- 异步组件-默认渲染的页面 --&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;Children :msg=&quot;msg&quot; :list=&quot;list&quot; @changeMsg=&quot;changeMsg&quot;&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;template #fallback&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;!-- 页面还没加载出来展示的页面 --&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      &lt;div&gt;loading...&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/Suspense&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, reactive, defineAsyncComponent } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">const Children = defineAsyncComponent(() =&gt; import(&#39;./Children.vue&#39;))</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="teleport传送组件" tabindex="-1">Teleport传送组件 <a class="header-anchor" href="#teleport传送组件" aria-label="Permalink to &quot;Teleport传送组件&quot;">​</a></h2><p>Teleport 是一种能够将我们的模板渲染至指定DOM节点，不受父级style、v-show等属性影响，但data、prop数据依旧能够共用的技术</p><p>主要解决的问题：因为Teleport节点挂载在其他指定的DOM节点下，完全不受父级style样式影响</p><p>使用： 通过to 属性插入到指定元素位置，如 body，html，自定义className等等。</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;!-- 插入至 body --&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Teleport to=&quot;body&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;Children&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/Teleport&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;!-- 默认 #app 下 --&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Children&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script lang=&quot;ts&quot; setup&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import Children from &#39;./Children.vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ic5A4V8PX4PniaElFR8nqkPVh6iajiaF7ytvqs1uvgiaTjYQDsSxzV6vVvK0fC5ibFP2xiaTEM7baQ7VRicRbG2BDQerHw/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><h2 id="keep-alive-缓存组件" tabindex="-1">keep-alive 缓存组件 <a class="header-anchor" href="#keep-alive-缓存组件" aria-label="Permalink to &quot;keep-alive 缓存组件&quot;">​</a></h2><ul><li>作用和vue2一致，只是生命周期名称有所更改</li><li>初次进入时： onMounted&gt; onActivated</li><li>退出后触发 deactivated</li><li>再次进入：只会触发 onActivated</li></ul><p>事件挂载的方法等，只执行一次的放在 onMounted中；组件每次进去执行的方法放在 onActivated中</p><h2 id="provide-inject" tabindex="-1">provide/inject <a class="header-anchor" href="#provide-inject" aria-label="Permalink to &quot;provide/inject&quot;">​</a></h2><p>provide 可以在祖先组件中指定我们想要提供给后代组件的数据或方法，而在任何后代组件中，我们都可以使用 inject 来接收 provide 提供的数据或方法。</p><p>父组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Children&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref, provide } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import Children from &quot;./Children.vue&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const msg = ref(&#39;hello 啊，树哥&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">provide(&#39;msg&#39;, msg)</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>子组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;msg：{{msg}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;button @click=&quot;onChangeMsg&quot;&gt;改变msg&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { inject, Ref, ref } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const msg = inject&lt;Ref&lt;string&gt;&gt;(&#39;msg&#39;,ref(&#39;hello啊！&#39;))</span></span>
<span class="line"><span style="color:#A6ACCD;">const onChangeMsg = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg.value = &#39;shuge&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>如果你想要传入的值能响应式的改变，需要通过ref 或 reactive 添加响应式</p><h2 id="v-model-升级" tabindex="-1">v-model 升级 <a class="header-anchor" href="#v-model-升级" aria-label="Permalink to &quot;v-model 升级&quot;">​</a></h2><p>v-model 在vue3可以说是破坏式更新，改动还是不少的</p><p>我们都知道，v-model 是props 和 emit 组合而成的语法糖,vue3中 v-model 有以下改动</p><ul><li>变更：value =&gt; modelValue</li><li>变更：update:input =&gt; update:modelValue</li><li>新增：一个组件可以设置多个 v-model</li><li>新增：开发者可以自定义 v-model修饰符</li><li>v-bind 的 .sync 修饰符和组件的 model 选项已移除</li></ul><p>子组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;{{msg}}，{{modelValue}}&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;button @click=&quot;onChangeMsg&quot;&gt;改变msg&lt;/button&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">type Props = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  modelValue: string,</span></span>
<span class="line"><span style="color:#A6ACCD;">  msg: string</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">defineProps&lt;Props&gt;()</span></span>
<span class="line"><span style="color:#A6ACCD;">const emit = defineEmits([&#39;update:modelValue&#39;, &#39;update:msg&#39;])</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const onChangeMsg = () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 触发父组件的值更新</span></span>
<span class="line"><span style="color:#A6ACCD;">  emit(&#39;update:modelValue&#39;, &#39;恰如此时此刻&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">  emit(&#39;update:msg&#39;, &#39;彼时彼刻&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><p>父组件</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  // v-model:modelValue简写为v-model</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 绑定多个v-model</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;Children v-model=&quot;name&quot; v-model:msg=&quot;msg&quot;&gt;&lt;/Children&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { ref } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">import Children from &quot;./Children.vue&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">const msg = ref(&#39;hello啊&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">const name = ref(&#39;树哥&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="自定义指令" tabindex="-1">自定义指令 <a class="header-anchor" href="#自定义指令" aria-label="Permalink to &quot;自定义指令&quot;">​</a></h2><p>自定义指令的生命周期</p><ul><li>created 元素初始化的时候</li><li>beforeMount 指令绑定到元素后调用 只调用一次</li><li>mounted 元素插入父级dom调用</li><li>beforeUpdate 元素被更新之前调用</li><li>update 这个周期方法被移除 改用updated</li><li>beforeUnmount 在元素被移除前调用</li><li>unmounted 指令被移除后调用 只调用一次</li></ul><p>实现一个自定义拖拽指令</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div v-move class=&quot;box&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;div class=&quot;header&quot;&gt;&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      内容</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"> </span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&#39;ts&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import { Directive } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;">const vMove: Directive = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  mounted(el: HTMLElement) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    let moveEl = el.firstElementChild as HTMLElement;</span></span>
<span class="line"><span style="color:#A6ACCD;">    const mouseDown = (e: MouseEvent) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">      //鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离</span></span>
<span class="line"><span style="color:#A6ACCD;">      console.log(e.clientX, e.clientY, &quot;起始位置&quot;, el.offsetLeft);</span></span>
<span class="line"><span style="color:#A6ACCD;">      let X = e.clientX - el.offsetLeft;</span></span>
<span class="line"><span style="color:#A6ACCD;">      let Y = e.clientY - el.offsetTop;</span></span>
<span class="line"><span style="color:#A6ACCD;">      const move = (e: MouseEvent) =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">        el.style.left = e.clientX - X + &quot;px&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;">        el.style.top = e.clientY - Y + &quot;px&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;">        console.log(e.clientX, e.clientY, &quot;位置改变&quot;);</span></span>
<span class="line"><span style="color:#A6ACCD;">      };</span></span>
<span class="line"><span style="color:#A6ACCD;">      document.addEventListener(&quot;mousemove&quot;, move);</span></span>
<span class="line"><span style="color:#A6ACCD;">      document.addEventListener(&quot;mouseup&quot;, () =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">        document.removeEventListener(&quot;mousemove&quot;, move);</span></span>
<span class="line"><span style="color:#A6ACCD;">      });</span></span>
<span class="line"><span style="color:#A6ACCD;">    };</span></span>
<span class="line"><span style="color:#A6ACCD;">    moveEl.addEventListener(&quot;mousedown&quot;, mouseDown);</span></span>
<span class="line"><span style="color:#A6ACCD;">  },</span></span>
<span class="line"><span style="color:#A6ACCD;">};</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"> </span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;style &gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">.box {</span></span>
<span class="line"><span style="color:#A6ACCD;">  position: fixed;</span></span>
<span class="line"><span style="color:#A6ACCD;">  left: 50%;</span></span>
<span class="line"><span style="color:#A6ACCD;">  top: 50%;</span></span>
<span class="line"><span style="color:#A6ACCD;">  transform: translate(-50%, -50%);</span></span>
<span class="line"><span style="color:#A6ACCD;">  width: 200px;</span></span>
<span class="line"><span style="color:#A6ACCD;">  height: 200px;</span></span>
<span class="line"><span style="color:#A6ACCD;">  border: 1px solid #ccc;</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">.header {</span></span>
<span class="line"><span style="color:#A6ACCD;">  height: 20px;</span></span>
<span class="line"><span style="color:#A6ACCD;">  background: black;</span></span>
<span class="line"><span style="color:#A6ACCD;">  cursor: move;</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/style&gt;</span></span></code></pre></div><h2 id="自定义-hooks" tabindex="-1">自定义 hooks <a class="header-anchor" href="#自定义-hooks" aria-label="Permalink to &quot;自定义 hooks&quot;">​</a></h2><p>我们都知道在 vue 中有个东西叫 mixins，他可以将多个组件中相同的逻辑抽离出来，实现一次写代码，多组件受益的效果。</p><p>但是 mixins 的副作用就是引用的多了变量的来源就不清晰了，而且还会有变量来源不明确,不利于阅读，容易使代码变得难以维护。</p><ul><li>Vue3 的 hook函数 相当于 vue2 的 mixin, 不同在与 hooks 是函数</li><li>Vue3 的 hook函数 可以帮助我们提高代码的复用性, 让我们能在不同的组件中都利用 hooks 函数</li></ul><p><strong>useWindowResize</strong></p><p>我们来实现一个窗口改变时获取宽高的 hook</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">import { onMounted, onUnmounted, ref } from &quot;vue&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">function useWindowResize() {</span></span>
<span class="line"><span style="color:#A6ACCD;">  const width = ref(0);</span></span>
<span class="line"><span style="color:#A6ACCD;">  const height = ref(0);</span></span>
<span class="line"><span style="color:#A6ACCD;">  function onResize() {</span></span>
<span class="line"><span style="color:#A6ACCD;">    width.value = window.innerWidth;</span></span>
<span class="line"><span style="color:#A6ACCD;">    height.value = window.innerHeight;</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">  onMounted(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">    window.addEventListener(&quot;resize&quot;, onResize);</span></span>
<span class="line"><span style="color:#A6ACCD;">    onResize();</span></span>
<span class="line"><span style="color:#A6ACCD;">  });</span></span>
<span class="line"><span style="color:#A6ACCD;">  onUnmounted(() =&gt; {</span></span>
<span class="line"><span style="color:#A6ACCD;">    window.removeEventListener(&quot;resize&quot;, onResize);</span></span>
<span class="line"><span style="color:#A6ACCD;">  });</span></span>
<span class="line"><span style="color:#A6ACCD;">  return {</span></span>
<span class="line"><span style="color:#A6ACCD;">    width,</span></span>
<span class="line"><span style="color:#A6ACCD;">    height</span></span>
<span class="line"><span style="color:#A6ACCD;">  };</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">export default useWindowResize;</span></span></code></pre></div><p>使用：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;h3&gt;屏幕尺寸&lt;/h3&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;宽度：{{ width }}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;div&gt;高度：{{ height }}&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script setup lang=&quot;ts&quot;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import useWindowResize from &quot;../hooks/useWindowResize.ts&quot;;</span></span>
<span class="line"><span style="color:#A6ACCD;">const { width, height } = useWindowResize();</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span></code></pre></div><h2 id="style-v-bind-css变量注入" tabindex="-1">style v-bind CSS变量注入 <a class="header-anchor" href="#style-v-bind-css变量注入" aria-label="Permalink to &quot;style v-bind CSS变量注入&quot;">​</a></h2><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  &lt;span&gt; style v-bind CSS变量注入&lt;/span&gt;  </span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/template&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;script lang=&quot;ts&quot; setup&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  import { ref } from &#39;vue&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">  const color = ref(&#39;red&#39;)</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/script&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;style scoped&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  span {</span></span>
<span class="line"><span style="color:#A6ACCD;">    /* 使用v-bind绑定组件中定义的变量 */</span></span>
<span class="line"><span style="color:#A6ACCD;">    color: v-bind(&#39;color&#39;);</span></span>
<span class="line"><span style="color:#A6ACCD;">  }  </span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/style&gt;</span></span></code></pre></div><h1 id="图解diff算法——vue篇" tabindex="-1">图解Diff算法——Vue篇 <a class="header-anchor" href="#图解diff算法——vue篇" aria-label="Permalink to &quot;图解Diff算法——Vue篇&quot;">​</a></h1><h2 id="一、虚拟dom" tabindex="-1">一、虚拟dom <a class="header-anchor" href="#一、虚拟dom" aria-label="Permalink to &quot;一、虚拟dom&quot;">​</a></h2><h3 id="_1-虚拟-dom-是什么" tabindex="-1"><strong>1. 虚拟</strong> <strong>dom</strong> <strong>是什么？</strong> <a class="header-anchor" href="#_1-虚拟-dom-是什么" aria-label="Permalink to &quot;**1. 虚拟** **dom** **是什么？**&quot;">​</a></h3><p>虚拟dom是一个对象，一个用js来模拟真实dom的对象；</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">// 真实的dom结构</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;ul id=&#39;list&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li class=&#39;item1&#39;&gt;111&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li class=&#39;item2&#39;&gt;222&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li class=&#39;item3&#39;&gt;333&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/ul&gt;</span></span></code></pre></div><p>那么上述dom结构，在虚拟dom中是如何进行展示的呢？</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">// 旧的虚拟dom结构</span></span>
<span class="line"><span style="color:#A6ACCD;">const oldVDom = { </span></span>
<span class="line"><span style="color:#A6ACCD;">     tagName: &#39;ul&#39;, // 标签名</span></span>
<span class="line"><span style="color:#A6ACCD;">     props: {  // 标签属性</span></span>
<span class="line"><span style="color:#A6ACCD;">        id: &#39;list&#39; </span></span>
<span class="line"><span style="color:#A6ACCD;">     },</span></span>
<span class="line"><span style="color:#A6ACCD;">     children: [ // 标签子节点</span></span>
<span class="line"><span style="color:#A6ACCD;">        { tagName: &#39;li&#39;, props: { class: &#39;item1&#39; }, children: [&#39;111&#39;] },</span></span>
<span class="line"><span style="color:#A6ACCD;">        { tagName: &#39;li&#39;, props: { class: &#39;item2&#39; }, children: [&#39;222&#39;] },</span></span>
<span class="line"><span style="color:#A6ACCD;">        { tagName: &#39;li&#39;, props: { class: &#39;item3&#39; }, children: [&#39;333&#39;] },</span></span>
<span class="line"><span style="color:#A6ACCD;">     ]</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>此时我修改一下真实的dom结构后：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;ul id=&#39;list&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li class=&#39;item1&#39;&gt;111&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li class=&#39;item2&#39;&gt;222&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li class=&#39;item3&#39;&gt;three-three&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/ul&gt;</span></span></code></pre></div><p>之后会生成新的虚拟dom：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight has-diff"><code><span class="line"><span style="color:#A6ACCD;">// 新的虚拟dom结构</span></span>
<span class="line"><span style="color:#A6ACCD;">const newVDom = { </span></span>
<span class="line"><span style="color:#A6ACCD;">     tagName: &#39;ul&#39;, // 标签名</span></span>
<span class="line"><span style="color:#A6ACCD;">     props: {  // 标签属性</span></span>
<span class="line"><span style="color:#A6ACCD;">        id: &#39;list&#39; </span></span>
<span class="line"><span style="color:#A6ACCD;">     },</span></span>
<span class="line"><span style="color:#A6ACCD;">     children: [ // 标签子节点</span></span>
<span class="line"><span style="color:#A6ACCD;">         // 在diff中，会通过patch发现此处两个节点没有变化，并将其复用</span></span>
<span class="line"><span style="color:#A6ACCD;">        { tagName: &#39;li&#39;, props: { class: &#39;item1&#39; }, children: [&#39;111&#39;] },</span></span>
<span class="line"><span style="color:#A6ACCD;">        { tagName: &#39;li&#39;, props: { class: &#39;item2&#39; }, children: [&#39;222&#39;] },</span></span>
<span class="line"><span style="color:#A6ACCD;">        // 在diff的过程中，会通过patch来找出此处发生了更改，并将其替换</span></span>
<span class="line"><span style="color:#A6ACCD;">        { tagName: &#39;li&#39;, props: { class: &#39;item3&#39; }, children: [&#39;three-three&#39;]},</span></span>
<span class="line"><span style="color:#A6ACCD;">     ]</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>此时看到的两个dom结构就是我们常说的 <strong>新旧虚拟dom</strong></p><h3 id="_2-为什么要有虚拟-dom-解决了什么问题" tabindex="-1"><strong>2. 为什么要有虚拟</strong> <strong>dom</strong> <strong>？解决了什么问题？</strong> <a class="header-anchor" href="#_2-为什么要有虚拟-dom-解决了什么问题" aria-label="Permalink to &quot;**2. 为什么要有虚拟** **dom** **？解决了什么问题？**&quot;">​</a></h3><p>在虚拟dom出现之前，我们都是<strong>jQuery</strong>一把梭（不多说了jQuery yyds）。</p><p>这里先来了解一下浏览器的渲染原理：</p><ol><li><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmc6uKwsyR4RXUm2dEyCY8ABLuEs75ib84g0I9CiaCnQtnTRsqk5Cd3Kfeg/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p></li><li></li><li><p>由图可以发现触发一次重排的代价还是比较大的；如果频繁触发浏览器的重排，无疑会造成很大的性能成本。</p></li></ol><p>我们都知道，在每一次事件循环后浏览器会有一个UI的渲染过程，那么在一次事件循环内触发的所有dom操作都会被当作为异步任务被放进异步任务队列中等待被处理。</p><p>那么此例子只是更改了一次dom结构，如果更改100+次呢？</p><p>虽然浏览器做了优化，在一段时间内频繁触发的dom不会被立即执行，浏览器会积攒变动以最高60HZ的频率更新视图；但是难免还是会造成一定次数的重排。</p><p>这时候，虚拟dom就派上了用场：不管更改多少次，多少个地方的结构，都会映射到新的虚拟dom结构中去，然后进行diff的对比，最终渲染成真实的dom，在这一次render中只会操作一次真实的dom结构，所以只会造成一次重排。</p><p>同时，采用JS对象去模拟DOM结构的好处是，页面的更新完全可以映射到JS对象中去处理，而操作内存中的JS对象速度也会更快。</p><p><strong>所以才有了虚拟dom的出现，可以看下图虚拟dom工作原理：</strong></p><ul><li>先根据初始的dom结构，生成一个 旧的虚拟dom：<strong>oldVDom</strong>；</li><li>再根据修改后的dom结构，生成 一个新的虚拟dom：<strong>newVDom</strong>；</li><li>然后通过<strong>diff算法</strong>来对比新旧虚拟DOM，从而找出需要替换的节点，然后将其渲染为真实的dom结构；</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcRz1eDjNqqrk8hBcT6eXVstRKIqpVEmdwPibxYwEl565wAWxia2OO9qrQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ul><li><h3 id="虚拟dom的缺点" tabindex="-1">虚拟dom的缺点？ <a class="header-anchor" href="#虚拟dom的缺点" aria-label="Permalink to &quot;虚拟dom的缺点？&quot;">​</a></h3></li></ul><p>看了上述虚拟dom的优点，我们来聊聊使用它的一些代价：</p><ol><li><p><strong>首屏加载时间更长</strong></p></li><li><p>由于我们需要根据当前的节点，来生成对应的虚拟dom，我们都知道虚拟dom是一个JS对象，所以在项目初始化的时候去生成对应的虚拟节点也是一笔时间上的开销；因此项目的首次加载可能耗费更多时间</p></li><li><p><strong>极端场景下性能不是最优解</strong></p></li><li><p>栗子🌰：如果当前页面的节点基本全都改变了，那我们去做了一次diff的patch过程相当于做了无效操作；</p></li></ol><h2 id="二、diff算法" tabindex="-1">二、Diff算法 <a class="header-anchor" href="#二、diff算法" aria-label="Permalink to &quot;二、Diff算法&quot;">​</a></h2><p>了解了虚拟dom结构之后，我们都清楚了diff的触发时机是在<strong>新旧VDom进行对比的时候</strong>。</p><p><em><strong>tips</strong></em>：既然所有的更改都被映射到了新的VDom上，那么为何不直接将新的VDom渲染成真实的dom呢？</p><p><em><strong>answer</strong></em>：如果直接渲染的话，会默认把所有节点都更新一遍，造成不必要的节点更新；而经过了diff的比较后可以精确的找出那些节点需要更新，从而实现按需更新的理念，节省性能；</p><p>那么Diff算法的比较规则有哪些呢？</p><h3 id="同层比较" tabindex="-1">同层比较 <a class="header-anchor" href="#同层比较" aria-label="Permalink to &quot;同层比较&quot;">​</a></h3><p>为什么要同层比较？</p><p>如果不同层比较的话，全部的对比完一整个dom结构，时间复杂度是 <strong>O(n^3)</strong> <strong>;</strong> 时间成本太大了；所以改用同层比较这样的方法去牺牲了精度而提高了时间效率。</p><p>可以看到图中每一层的节点，都是同层在进行对比，这样的好处就是，不会每一层的对比都是相对独立的，不会影响到下一层的对比；同时同层对比的时间复杂度也是 <strong>O(n)；</strong></p><p>同时也是遵循一个深度优先的原则；diff的过程是一个深度优先遍历节点，然后将该节点与newVDom中的同层节点进行对比，如果有差异，则记录该节点到JS对象中。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcZSZ5r56T6aXGgjEhb7JkurkZpFwJUx1VBxJxB6prhmpAnsSibqZeLYQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>在同层对比的过程中有这样几种情况：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;p&gt;ppp&lt;/p&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;ul id=&#39;list&#39; &gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;li class=&#39;item1&#39;&gt;111&lt;/li&gt;   </span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;li class=&#39;item2&#39;&gt;222&lt;/li&gt;  </span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;li class=&#39;item3&#39;&gt;333&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/ul&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;div&gt;div&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 1. 节点类型发生了改变</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;h3&gt;ppp&lt;/h3&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 2. 节点类型一样，属性发生变化</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;ul id=&#39;list-change&#39;&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;li class=&#39;item1&#39;&gt;111&lt;/li&gt;   </span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;li class=&#39;item2&#39;&gt;222&lt;/li&gt;  </span></span>
<span class="line"><span style="color:#A6ACCD;">        // 3. 节点被删除</span></span>
<span class="line"><span style="color:#A6ACCD;">        // &lt;li class=&#39;item3&#39;&gt;333&lt;/li&gt; </span></span>
<span class="line"><span style="color:#A6ACCD;">        // 4. 新增节点</span></span>
<span class="line"><span style="color:#A6ACCD;">        &lt;li class=&#39;item4&#39;&gt;444&lt;/li&gt;  </span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;/ul&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 4. 文本变化</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;div&gt;属性变化&lt;/div&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/div&gt;</span></span></code></pre></div><h4 id="_1-节点类型变了" tabindex="-1"><strong>1. 节点类型变了</strong> <a class="header-anchor" href="#_1-节点类型变了" aria-label="Permalink to &quot;**1. 节点类型变了**&quot;">​</a></h4><p>节点<code>p</code>标签 变成了<code>h3</code>标签，此时diff的过程中<code>p</code>节点会被直接销毁，然后挂载新的节点 <code>h3</code>，同时<code>p</code>标签的子节点也会被全部销毁；虽然可能造成一些不必要的销毁，但是为了实现同层比较的方法节省时间成本只能这样做咯；同时这样也告诫我们在写代码的时候，可以规避一些不必要的父节点的类型替换，比如将<code>p</code>标签换成了<code>div</code>等。</p><h4 id="_2-节点类型一样-属性或者属性值发生变化" tabindex="-1"><strong>2. 节点类型一样，属性或者属性值发生变化</strong> <a class="header-anchor" href="#_2-节点类型一样-属性或者属性值发生变化" aria-label="Permalink to &quot;**2. 节点类型一样，属性或者属性值发生变化**&quot;">​</a></h4><p>此时不会触发节点的卸载和挂载，只会触发当前节点的更新</p><h4 id="_3-删除-新增-改变-节点" tabindex="-1"><strong>3. 删除/新增/改变 节点</strong> <a class="header-anchor" href="#_3-删除-新增-改变-节点" aria-label="Permalink to &quot;**3. 删除/新增/改变 节点**&quot;">​</a></h4><p>这时候就需要找出这些节点并在newVDom中进行插入/删除，这个过程请看下面vue和react是如何利用key值来处理的吧！</p><h4 id="_4-文本变化" tabindex="-1"><strong>4. 文本变化</strong> <a class="header-anchor" href="#_4-文本变化" aria-label="Permalink to &quot;**4. 文本变化**&quot;">​</a></h4><p>只会触发文本的改变</p><h2 id="三、vue中的diff算法" tabindex="-1">三、vue中的diff算法 <a class="header-anchor" href="#三、vue中的diff算法" aria-label="Permalink to &quot;三、vue中的diff算法&quot;">​</a></h2><p>在了解了虚拟dom和diff算法的相关内容后，我们来看看各大框架中是如何做处理的吧！</p><h3 id="vue2-双端比较" tabindex="-1">vue2--双端比较 <a class="header-anchor" href="#vue2-双端比较" aria-label="Permalink to &quot;vue2--双端比较&quot;">​</a></h3><p>这里你需要提前了解vue2内部的响应式原理是如何运作的，推荐文章：<strong>vue2的响应式原理</strong>[1]。那么，当触发了vue的数据的响应式后，其内部的一系列变化又是如何呢？</p><p>首先，数据改变会触发 <strong>setter</strong>，然后调用 Dep.notify(), 并且通过<code>Dep.notify</code>去通知所有<code>订阅者Watcher</code> <strong>，</strong> 订阅者们就会调用<code>patch方法</code> <strong>，</strong> 给真实 DOM 打补丁，更新相应的视图。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmc7yPunkib6w3jwNF5YicwwqMgEtw1cDhnFtGHmzpe4yvIZ21OCse5ER3Q/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>接下来我们来分析几个核心函数吧：</p><h4 id="patch" tabindex="-1"><strong>patch</strong> <strong>()</strong> <a class="header-anchor" href="#patch" aria-label="Permalink to &quot;**patch** **()**&quot;">​</a></h4><p>diff的入口函数；</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">function patch(oldVnode, newVnode) { // 传入新、旧节点</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 比较是否为一个类型的节点</span></span>
<span class="line"><span style="color:#A6ACCD;">  if (sameVnode(oldVnode, newVnode)) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 是：继续进行深层比较</span></span>
<span class="line"><span style="color:#A6ACCD;">    patchVnode(oldVnode, newVnode)</span></span>
<span class="line"><span style="color:#A6ACCD;">  } else {</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 否</span></span>
<span class="line"><span style="color:#A6ACCD;">    const oldEl = oldVnode.el // 旧虚拟节点的真实DOM节点</span></span>
<span class="line"><span style="color:#A6ACCD;">    const parentEle = api.parentNode(oldEl) // 获取父节点</span></span>
<span class="line"><span style="color:#A6ACCD;">    createEle(newVnode) // 创建新虚拟节点对应的真实DOM节点</span></span>
<span class="line"><span style="color:#A6ACCD;">    if (parentEle !== null) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      api.insertBefore(parentEle, newVnode.el, api.nextSibling(oldEl)) // 将新元素添加进父元素</span></span>
<span class="line"><span style="color:#A6ACCD;">      api.removeChild(parentEle, oldVnode.el)  // 移除以前的旧元素节点</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 设置null，释放内存</span></span>
<span class="line"><span style="color:#A6ACCD;">      oldVnode = null</span></span>
<span class="line"><span style="color:#A6ACCD;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">  return newVnode</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><h4 id="samevnode" tabindex="-1"><strong>sameVNode</strong> <strong>()</strong> <a class="header-anchor" href="#samevnode" aria-label="Permalink to &quot;**sameVNode** **()**&quot;">​</a></h4><p>主要用来判断两个节点是否完全相同，那么满足什么条件才能判断两个节点完全相同呢？</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">function sameVnode(oldVnode, newVnode) {</span></span>
<span class="line"><span style="color:#A6ACCD;">  return (</span></span>
<span class="line"><span style="color:#A6ACCD;">    oldVnode.key === newVnode.key &amp;&amp; // key值是否一样</span></span>
<span class="line"><span style="color:#A6ACCD;">    oldVnode.tagName === newVnode.tagName &amp;&amp; // 标签名是否一样</span></span>
<span class="line"><span style="color:#A6ACCD;">    oldVnode.isComment === newVnode.isComment &amp;&amp; // 是否都为注释节点</span></span>
<span class="line"><span style="color:#A6ACCD;">    isDef(oldVnode.data) === isDef(newVnode.data) &amp;&amp; // 是否都定义了data</span></span>
<span class="line"><span style="color:#A6ACCD;">    sameInputType(oldVnode, newVnode) // 当标签为input时，type必须是否相同</span></span>
<span class="line"><span style="color:#A6ACCD;">  )</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><h4 id="patchvnode" tabindex="-1"><strong>patchVNode</strong> <strong>()</strong> <a class="header-anchor" href="#patchvnode" aria-label="Permalink to &quot;**patchVNode** **()**&quot;">​</a></h4><p><strong>此阶段我们已经找到了需要去对比的节点，那么该方法主要做了什么呢？</strong></p><ul><li><p>拿到真实的dom节点<code>el</code>（即<code>oldVnode</code>）</p></li><li><p>判断当前<code>newVnode</code>和<code>oldVnode</code>是否指向同一个对象，如果是则直接return</p></li><li><p>如果是文本节点，且文本有变化，则直接调用api 将文本替换；若文本没有变化，则继续对比新旧节点的子节点<code>children</code></p></li><li><p>如果<code>oldVnode</code>有子节点而<code>newVnode</code>没有，则删除<code>el</code>的子节点</p></li><li><p>如果<code>oldVnode</code>没有子节点而<code>newVnode</code>有，则将<code>newVnode</code>的子节点真实化之后添加到<code>el</code></p></li><li><p>如果两者都有子节点，则执行<code>updateChildren</code>函数比较子节点，这一步很重要---<strong>diff的核心</strong></p></li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight has-diff"><code><span class="line"><span style="color:#A6ACCD;">function patchVnode(oldVnode, newVnode) {</span></span>
<span class="line"><span style="color:#A6ACCD;">  const el = newVnode.el = oldVnode.el // 获取真实DOM对象</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 获取新旧虚拟节点的子节点数组</span></span>
<span class="line"><span style="color:#A6ACCD;">  const oldCh = oldVnode.children, newCh = newVnode.children</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 如果新旧虚拟节点是同一个对象，则终止</span></span>
<span class="line"><span style="color:#A6ACCD;">  if (oldVnode === newVnode) return</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 如果新旧虚拟节点是文本节点，且文本不一样</span></span>
<span class="line"><span style="color:#A6ACCD;">  if (oldVnode.text !== null &amp;&amp; newVnode.text !== null &amp;&amp; oldVnode.text !== newVnode.text) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 则直接将真实DOM中文本更新为新虚拟节点的文本</span></span>
<span class="line"><span style="color:#A6ACCD;">    api.setTextContent(el, newVnode.text)</span></span>
<span class="line"><span style="color:#A6ACCD;">  } else {</span></span>
<span class="line"><span style="color:#A6ACCD;">    if (oldCh &amp;&amp; newCh &amp;&amp; oldCh !== newCh) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 新旧虚拟节点都有子节点，且子节点不一样</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 对比子节点，并更新</span></span>
<span class="line"><span style="color:#A6ACCD;">      /*  diff核心！！*/  </span></span>
<span class="line"><span style="color:#A6ACCD;">      updateChildren(el, oldCh, newCh) </span></span>
<span class="line"><span style="color:#A6ACCD;">    } else if (newCh) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 新虚拟节点有子节点，旧虚拟节点没有</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 创建新虚拟节点的子节点，并更新到真实DOM上去</span></span>
<span class="line"><span style="color:#A6ACCD;">      createEle(newVnode)</span></span>
<span class="line"><span style="color:#A6ACCD;">    } else if (oldCh) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 旧虚拟节点有子节点，新虚拟节点没有</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 直接删除真实DOM里对应的子节点</span></span>
<span class="line"><span style="color:#A6ACCD;">      api.removeChild(el)</span></span>
<span class="line"><span style="color:#A6ACCD;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><h4 id="updatechildren" tabindex="-1"><strong>updateChildren</strong> <strong>()</strong> <a class="header-anchor" href="#updatechildren" aria-label="Permalink to &quot;**updateChildren** **()**&quot;">​</a></h4><p>此方法就是diff算法的核心部分，当发现新旧虚拟节点的的子节点都存在时候，我们就需要通过一些方法来判断哪些节点是需要移动的，哪些节点是可以直接复用的，来提高我们整个diff的效率；</p><p>下面就通过一些图解来讲解吧：</p><ul><li>主要是通过 <strong>首尾指针法 ：</strong> 通过在新旧子节点的首尾定义四个指针，然后不断的对比找到可复用的节点，同时判断需要移动的节点。</li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;ul&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;a&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;b&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;c&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;b&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"> &lt;/ul&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">修改数据后 ⬇️ // a,b,c,d  -&gt;  d,b,a,c</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;ul&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;d&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;b&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;a&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li&gt;c&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/ul&gt;</span></span></code></pre></div><h5 id="_1、理想情况下" tabindex="-1">1、理想情况下 <a class="header-anchor" href="#_1、理想情况下" aria-label="Permalink to &quot;1、理想情况下&quot;">​</a></h5><p>经过四次比较可以找到替换的节点，可以看到图中第四次找到了可替换的节点；</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcCvNjwLXnvGHdTAaA2e0375D9icNq19XiadWFE4sfg4QoTJCcOlE1QhdQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>可以看到在 oldCh 和 newCh 的首尾定义了四个指针：</p><ul><li><p>1、<code>oldS</code>和 <code>newS</code>使用<code>sameVnode方法</code>进行比较，<code>sameVnode(oldS, newS)</code> ；如果相同，则 <code>oldS</code>++，<code>newS</code>++</p></li><li><p>2、<code>oldE</code> 和<code>newE</code>使用<code>sameVnode方法</code>进行比较，<code>sameVnode(oldE, newE)</code>；如果相同，则 <code>oldE</code>--，<code>newS</code> --</p></li><li><p>3、<code>oldS</code>和 <code>newE</code>使用<code>sameVnode方法</code>进行比较，<code>sameVnode(oldS, newE)</code>；如果相同，则 <code>oldS</code> ++，<code>newS</code> --</p></li><li><p>4、<code>oldE</code> 和 <code>newS</code>使用<code>sameVnode方法</code>进行比较，<code>sameVnode(oldE, newS)</code>；如果相同，则 <code>oldE</code> --，<code>newS</code> ++</p></li></ul><p>这是一个不断向内部收缩的过程，直到对比完所有的节点；</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">function vue2Diff(prevChildren, nextChildren, parent) {</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 在新旧首尾，分别定义四个指针</span></span>
<span class="line"><span style="color:#A6ACCD;">  let oldStartIndex = 0,</span></span>
<span class="line"><span style="color:#A6ACCD;">    oldEndIndex = prevChildren.length - 1</span></span>
<span class="line"><span style="color:#A6ACCD;">    newStartIndex = 0,</span></span>
<span class="line"><span style="color:#A6ACCD;">    newEndIndex = nextChildren.length - 1;</span></span>
<span class="line"><span style="color:#A6ACCD;">  let oldStartNode = prevChildren[oldStartIndex],</span></span>
<span class="line"><span style="color:#A6ACCD;">    oldEndNode = prevChildren[oldEndIndex],</span></span>
<span class="line"><span style="color:#A6ACCD;">    newStartNode = nextChildren[newStartIndex],</span></span>
<span class="line"><span style="color:#A6ACCD;">    newEndNode = nextChildren[newEndIndex];</span></span>
<span class="line"><span style="color:#A6ACCD;">   // 不断向内收缩</span></span>
<span class="line"><span style="color:#A6ACCD;">  while (oldStartIndex &lt;= oldEndIndex &amp;&amp; newStartIndex &lt;= newEndIndex) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      if (oldStartNode.key === newStartNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">        ...</span></span>
<span class="line"><span style="color:#A6ACCD;">      } else if (oldEndNode.key === newEndNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">        ...</span></span>
<span class="line"><span style="color:#A6ACCD;">      } else if (oldStartNode.key === newEndNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">        ...</span></span>
<span class="line"><span style="color:#A6ACCD;">      } else if (oldEndNode.key === newStartNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">        ...</span></span>
<span class="line"><span style="color:#A6ACCD;">      }</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>在经历了上面的循环后，我们可以找出一些节点并将其复用，但是我们复用的过程中，需要怎么插入这些节点呢？</p><p>以上图中的为第一步，我们可以发现，<strong>d</strong> 节点<strong>原本在旧列表末尾的节点，却是新列表中的开头节点，没有人比它更靠前，因为他是第一个，所以我们只需要把当前的节点移动到原本旧列表中的第一个节点之前，让它成为第一个节点即可</strong>。</p><p>第二步：</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmc3NvicM8YmtSvqPkaXyl3U8KbNYFKqJ0N7PAfdfQkXJbcdCXrvQ6Ujeg/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>第二步我们可以发现了key相同的 <strong>c</strong> 节点，<strong>旧列表的尾节点</strong><code>oldE</code>和<strong>新列表的尾节点</strong><code>newE</code>为复用节点。<strong>原本在旧列表中就是尾节点，在新列表中也是尾节点，说明该节点不需要移动</strong>，所以我们什么都不需要做。</p><p>第三步：</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcMLeEoJ6aqmBeYWr5iav9ExruHOJiauLKQv2dQXfN2MFKkJAXia8n73DcA/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>在第三步中我们可以看到 <strong>a</strong> 节点是可以复用的，<strong>旧列表的头节点</strong><code>oldS</code>和<strong>新列表的尾节点</strong><code>newE</code>为复用节点，我们只要将<code>DOM-a</code>移动到<code>DOM-b</code>后面就可以了。<strong>原本旧列表中是头节点，然后在新列表中是尾节点。那么只要在旧列表中把当前的节点移动到原本尾节点的后面，就可以了</strong>。</p><p>第四步：这一步不需要移动节点，直接复用；</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcbdWApFZSDmCLRFcsMqaCU9zKXibOnAG0dI0CZXtsP5bmoCfV23GqXyw/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>最终呢，我们就得到了对比后的 dbac 啦，同时发现只有 d 和 a 节点需要进行移动，而b 、c节点都是不需要移动的；那么至此，一个理想状态下的diff比较过程就结束了，是不是感觉很清晰易懂呢？</p><h5 id="_2、非理想状态下" tabindex="-1">2、非理想状态下 <a class="header-anchor" href="#_2、非理想状态下" aria-label="Permalink to &quot;2、非理想状态下&quot;">​</a></h5><ul><li>如果这四种方式都没有找到该怎么处理呢？</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcPLSO2kM5bMZia43HA9MicrjnrGCFznIqqwDpExgtwOF3viaLdlWQCLcibA/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>可以看到图中四次比较都没有找到可以复用的节点，那么我们只能把所有旧子节点的 <code>key</code> 做一个映射到旧节点下标的 <code>key -&gt; index</code> 表，然后用新 <code>vnode</code> 的 <code>key</code> 去找出在旧节点中可以复用的位置；可以看下图的处理。拿<strong>新列表</strong>的第一个节点去<strong>旧列表</strong>中找与其<code>key</code>相同的节点。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcX6OeN9w5vj7945dFOfS1qY8QzfcMnibh8icaGMXXXIUTD3asD8zDW2Og/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>那么我们就以 <code>newCh</code> 的首节点的key值，去到 <code>oldCh</code> 的 <code>key - index</code> 的映射表中，去根据<code>key</code>值找到对应的节点，同时将 <strong>b</strong> 节点移动到首部去，因为在新列表中 <strong>b</strong> 就属于首部，所以在<code>oldCh</code>中也需要移动到首部 ；同时，还需要将 <code>oldCh</code> 中的 <strong>b</strong> 节点设为 <strong>undefined</strong> , 因为已经复用过了，就可以跳过比较了。</p><p>这个非理想的状态下的对比时间复杂度为 <strong>O(n^2):</strong></p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">function vue2Diff(prevChildren, nextChildren, parent) {</span></span>
<span class="line"><span style="color:#A6ACCD;">  //...</span></span>
<span class="line"><span style="color:#A6ACCD;">  while (oldStartIndex &lt;= oldEndIndex &amp;&amp; newStartIndex &lt;= newEndIndex) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    if (oldStartNode.key === newStartNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    //...</span></span>
<span class="line"><span style="color:#A6ACCD;">    } else if (oldEndNode.key === newEndNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    //...</span></span>
<span class="line"><span style="color:#A6ACCD;">    } else if (oldStartNode.key === newEndNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    //...</span></span>
<span class="line"><span style="color:#A6ACCD;">    } else if (oldEndNode.key === newStartNode.key) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    //...</span></span>
<span class="line"><span style="color:#A6ACCD;">    } else {</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 在旧列表中找到 和新列表头节点key 相同的节点</span></span>
<span class="line"><span style="color:#A6ACCD;">      let newtKey = newStartNode.key,</span></span>
<span class="line"><span style="color:#A6ACCD;">        oldIndex = prevChildren.findIndex(child =&gt; child.key === newKey);</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span></span>
<span class="line"><span style="color:#A6ACCD;">      if (oldIndex &gt; -1) {</span></span>
<span class="line"><span style="color:#A6ACCD;">        let oldNode = prevChildren[oldIndex];</span></span>
<span class="line"><span style="color:#A6ACCD;">        patch(oldNode, newStartNode, parent)</span></span>
<span class="line"><span style="color:#A6ACCD;">        parent.insertBefore(oldNode.el, oldStartNode.el)</span></span>
<span class="line"><span style="color:#A6ACCD;">        // 复用后，设置为 undefined </span></span>
<span class="line"><span style="color:#A6ACCD;">        prevChildren[oldIndex] = undefined</span></span>
<span class="line"><span style="color:#A6ACCD;">      }</span></span>
<span class="line"><span style="color:#A6ACCD;">      newStartNode = nextChildren[++newStartIndex]</span></span>
<span class="line"><span style="color:#A6ACCD;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><h3 id="vue3-最长递增子序列" tabindex="-1">vue3--最长递增子序列 <a class="header-anchor" href="#vue3-最长递增子序列" aria-label="Permalink to &quot;vue3--最长递增子序列&quot;">​</a></h3><p>那么相比vue2中的双端对比，在vue3中的diff算法，又做了哪些优化呢？</p><p>以下面的例子来看：</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcbHzl74tXOSficpRNSSNvNCBMvmbmHBZlnx8TkMmDxNy7837YgMWBx3w/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p><strong>1. 从头对比找到有相同的节点 patch ，发现不同，立即跳出。</strong></p><p><strong>2. 如果第一步没有patch完，立即，从后往前开始patch ,如果发现不同立即跳出循环。</strong></p><p><strong>3. 如果新的节点大于老的节点数 ，对于剩下的节点全部以新的vnode处理（这种情况说明已经patch完相同的vnode）。</strong></p><p><strong>4. 对于老的节点大于新的节点的情况 ， 对于超出的节点全部卸载（这种情况说明已经patch完相同的vnode）。</strong></p><p><strong>5. 不确定的元素（这种情况说明没有patch完相同的vnode） 与 3 ，4对立关系。</strong></p><p>前面的逻辑跟vue2还是比较像，逐渐向中间收缩，那么关键点就在判断哪些节点是需要变动的。</p><p>在经历上述操作后，会出现以下节点需要判断（即图中圈起来的节点）：</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcNTVeEW6mx9pApibCZKULDEQ05Vlr6VRiaibMaicOMkd9JXOKhU6rj2iaozQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>首先，我们以<strong>新节点</strong>的数量创建一个 <code>source</code> 数组，并用 <strong>-1</strong> 填满；</p><p>这个<code>source</code>数组就是用来做新旧节点的对应关系的，我们将<strong>新节点</strong>在<strong>旧列表</strong>的位置存储在该数组中，我们再根据<code>source</code>计算出它的<code>最长递增子序列</code>用于移动DOM节点。</p><p>其次，我们先建立一个对象存储当前<strong>新列表</strong>中的<code>节点</code>与<code>index</code>的关系：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">const newVNodeMap = {</span></span>
<span class="line"><span style="color:#A6ACCD;">    c: &#39;1&#39;, </span></span>
<span class="line"><span style="color:#A6ACCD;">    d: &#39;2&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    b: &#39;3&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    i: &#39;4&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>然后再去<strong>旧列表</strong>中去找相同的节点，并记录其<code>index</code>的位置。</p><p>在找节点时，<strong>如果旧节点在新列表中没有的话，直接删除就好</strong>。除此之外，我们还需要一个数量表示记录我们已经<code>patch</code>过的节点，如果数量已经与<strong>新列表</strong>剩余的节点数量一样，那么剩下的<code>旧节点</code>我们就直接删除了就可以了。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcTUJbf9JlpufTNUHHQbwG8MkJwtvnJuaDiaz5vQfhpucJ0shADHBqR6g/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><h4 id="dom如何移动" tabindex="-1">Dom如何移动？ <a class="header-anchor" href="#dom如何移动" aria-label="Permalink to &quot;Dom如何移动？&quot;">​</a></h4><p>首先，我们需要定义一个Lis数组来存储source中的最长连续递增子序列的下标：- 然后从后往前遍历 <strong>source</strong> 数组；这个过程中会发生<strong>三种情况</strong>：</p><ul><li>当前数值为 <strong>-1</strong> ，也就说明该节点是<strong>新增</strong>的，我们直接将其插入到队尾就好了，同时 <strong>i--。</strong></li><li>当前的索引和 Lis 中的值一致，即 <strong>i == Lis[j]</strong> ，同时 <strong>i --, j --。</strong></li><li>当前的索引<strong>不是 Lis 中的值</strong>，那么该节点就需要进行移动，我们只需要将该节点<strong>插入到队尾</strong>就可以了，因为队尾是排好序的。</li></ul><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcibD5DZ4vqBibH6cHpnVAL0OJ2J8pvd4gtBLZicb4Lw2TXOgB2C9spqINg/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p><em><strong>tips：没看懂这三种情况？不要慌：</strong></em></p><p>我们来一步一步拆解：</p><ol><li>首先，i = 3，即上图中，<strong>值为 -1</strong> 为第一种情况，节点需要新增，<strong>i--</strong>；</li></ol><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcZ4dqyCM8WYVwxJwIXIR8d4aakHiboPCNeQkwTK6mIahsAHwLZCWUV6A/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ol><li>i = 2，索引为 <strong>2 != Lis[j]</strong> ****为第三种情况，<strong>节点需要移动</strong>，直接在旧列表中，将b节点<strong>插入到尾部</strong>位置，<strong>i --</strong></li></ol><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcWdkCBia7jzibevU9CCxAiaraLOfLTKsiaDbG1iaVhn4VqVJfOGkmlIXpWbg/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcOib0ZMHrZ278fvJ0jBuO31LSvRgT0F1RAzo1XCubx81FpeayL31kBWA/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ol><li>i = 1，此时索引 i == Lis[j] 为第二种情况，我们的节点不需要移动；</li></ol><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcHmG6UbfibQkQic8Sicj5HXiaxtcRyqXBFrlkWibUhumicfUgmdCibQMTHibadQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><ol><li>i = 0，此时索引 i == Lis[j] 为第二种情况，我们的节点也不需要移动；</li></ol><p>至此 vue3的diff的对比过程就已经完成了，相比于2中的首尾指针法，在这种<strong>非理想情况下</strong>的节点对比采用了<strong>最长递增子序列</strong>的算法思想来做处理；</p><p><strong>这三种情况对应在源码中</strong> <strong>：</strong></p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">function vue3Diff(prevChildren, nextChildren, parent) {</span></span>
<span class="line"><span style="color:#A6ACCD;">  //...</span></span>
<span class="line"><span style="color:#A6ACCD;">  if (move) {</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 需要移动</span></span>
<span class="line"><span style="color:#A6ACCD;">    const seq = lis(source); // [0, 1]</span></span>
<span class="line"><span style="color:#A6ACCD;">    let j = seq.length - 1;  // 最长子序列的指针</span></span>
<span class="line"><span style="color:#A6ACCD;">    // 从后向前遍历</span></span>
<span class="line"><span style="color:#A6ACCD;">    for (let i = nextLeft - 1；i &gt;= 0; i--) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      let pos = nextStart + i, // 对应新列表的index</span></span>
<span class="line"><span style="color:#A6ACCD;">        nextNode = nextChildren[pos], // 找到vnode</span></span>
<span class="line"><span style="color:#A6ACCD;">        nextPos = pos + 1，    // 下一个节点的位置，用于移动DOM</span></span>
<span class="line"><span style="color:#A6ACCD;">        refNode = nextPos &gt;= nextChildren.length ? null : nextChildren[nextPos].el, //DOM节点</span></span>
<span class="line"><span style="color:#A6ACCD;">        cur = source[i];      // 当前source的值，用来判断节点是否需要移动</span></span>
<span class="line"><span style="color:#A6ACCD;">      if (cur === -1) {</span></span>
<span class="line"><span style="color:#A6ACCD;">        // 情况1，该节点是全新节点</span></span>
<span class="line"><span style="color:#A6ACCD;">        mount(nextNode, parent, refNode)</span></span>
<span class="line"><span style="color:#A6ACCD;">      } else if (cur === seq[j]) {</span></span>
<span class="line"><span style="color:#A6ACCD;">        // 情况2，是递增子序列，该节点不需要移动</span></span>
<span class="line"><span style="color:#A6ACCD;">        // 让j指向下一个</span></span>
<span class="line"><span style="color:#A6ACCD;">        j--</span></span>
<span class="line"><span style="color:#A6ACCD;">      } else {</span></span>
<span class="line"><span style="color:#A6ACCD;">        // 情况3，不是递增子序列，该节点需要移动</span></span>
<span class="line"><span style="color:#A6ACCD;">        parent.insetBefore(nextNode.el, refNode)</span></span>
<span class="line"><span style="color:#A6ACCD;">      }</span></span>
<span class="line"><span style="color:#A6ACCD;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">  } else {</span></span>
<span class="line"><span style="color:#A6ACCD;">  // 不需要移动</span></span>
<span class="line"><span style="color:#A6ACCD;">  for (let i = nextLeft - 1；i &gt;= 0; i--) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      let cur = source[i];              // 当前source的值，用来判断节点是否需要移动</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span></span>
<span class="line"><span style="color:#A6ACCD;">      if (cur === -1) {</span></span>
<span class="line"><span style="color:#A6ACCD;">       let pos = nextStart + i,         // 对应新列表的index</span></span>
<span class="line"><span style="color:#A6ACCD;">          nextNode = nextChildren[pos], // 找到vnode</span></span>
<span class="line"><span style="color:#A6ACCD;">          nextPos = pos + 1，           // 下一个节点的位置，用于移动DOM</span></span>
<span class="line"><span style="color:#A6ACCD;">          refNode = nextPos &gt;= nextChildren.length ? null : nextChildren[nextPos].el, //DOM节点</span></span>
<span class="line"><span style="color:#A6ACCD;">          mount(nextNode, parent, refNode)</span></span>
<span class="line"><span style="color:#A6ACCD;">      }</span></span>
<span class="line"><span style="color:#A6ACCD;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>你可能会问，你这边递增的子序列需要连续吗，那么这里给你将例子稍微变动一下：这时候你会发现连续递增的节点是 <strong>c, d, e</strong> 他们不是紧密连续的，<strong>但是在整个list中却是保持index递增的，也不需要移动。</strong></p><h5 id="思考题" tabindex="-1">思考题 <a class="header-anchor" href="#思考题" aria-label="Permalink to &quot;思考题&quot;">​</a></h5><p>参考上面的图解，结合源码，看看下面例子中的虚拟dom节点是怎么移动的。</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcAlN91TRgZRabBUvJUkILpq4oCrIwLsicF3qQWibsL5H5kiafZnDLHichiaw/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><h4 id="时间复杂度的优化" tabindex="-1">时间复杂度的优化 <a class="header-anchor" href="#时间复杂度的优化" aria-label="Permalink to &quot;时间复杂度的优化&quot;">​</a></h4><p>这里我们只需要找出source中的最长连续递增子序列 就ok了：</p><ul><li><p><strong>最长连续递增子序列</strong></p></li><li><p>直接放一道leetcode吧：<strong>最长递增子序列</strong>[2]</p></li></ul><p>举个例子：<strong>[10,5,6,7,4,1,2,8,9]</strong></p><p>那么在该此例子中，连续递增的子序列是 <strong>[5,6,7,8,9],</strong> 所以返回的个数是5；</p><p>可以参考该算法的基础实现：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">const arr = [10,5,6,7,4,1,2,8,9]</span></span>
<span class="line"><span style="color:#A6ACCD;">function lis(arr) {</span></span>
<span class="line"><span style="color:#A6ACCD;">  let len = arr.length,</span></span>
<span class="line"><span style="color:#A6ACCD;">    dp = new Array(len).fill(1); // 用于保存长度</span></span>
<span class="line"><span style="color:#A6ACCD;">  // i = 0 =&gt; O(n^2) ;  i != 0 =&gt;  O(nlogn)</span></span>
<span class="line"><span style="color:#A6ACCD;">  for (let i = len - 1; i &gt;= 0; i--) { </span></span>
<span class="line"><span style="color:#A6ACCD;">    let cur = arr[i]</span></span>
<span class="line"><span style="color:#A6ACCD;">    for(let j = i + 1; j &lt; len; j++) {</span></span>
<span class="line"><span style="color:#A6ACCD;">      let next = arr[j]</span></span>
<span class="line"><span style="color:#A6ACCD;">      // 如果是递增 取更大的长度值</span></span>
<span class="line"><span style="color:#A6ACCD;">      if (cur &lt; next) dp[i] = Math.max(dp[j]+1, dp[i])</span></span>
<span class="line"><span style="color:#A6ACCD;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">  return Math.max(...dp)</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span>
<span class="line"><span style="color:#A6ACCD;">lis(arr) // 5</span></span></code></pre></div><p>由算法可以看出：在最好的情况下即 <strong><code>i != 0</code></strong> 的条件下，平均的时间复杂度是<strong>O(nlgn)</strong> 那么在 <strong><code>i = 0</code></strong> 时，时间复杂度为<strong>O(n^2)</strong></p><p>在vue2中关于的这种节点的查找和替换的时间复杂度稳定为<strong>O(n^2)</strong></p><p>在vue3中依赖于最长递增子序列去做节点的移动和删除/新增，时间复杂度为<strong>O(nlgn)～O(n^2)</strong></p><ul><li><p>至此vue3.0的diff算法大致理念以及概括完了，如果想要深入了解可以去阅读以下源码部分</p></li><li><p><strong>vue3 diff 源码</strong>[3]</p></li></ul><h2 id="四、key值的作用" tabindex="-1">四、key值的作用 <a class="header-anchor" href="#四、key值的作用" aria-label="Permalink to &quot;四、key值的作用&quot;">​</a></h2><blockquote><p>为什么不能使用index作为key值？</p></blockquote><h3 id="key的作用-性能更高" tabindex="-1">key的作用--性能更高 <a class="header-anchor" href="#key的作用-性能更高" aria-label="Permalink to &quot;key的作用--性能更高&quot;">​</a></h3><blockquote><p>在Vue中判断节点是否可复用都是以key值作为判断的前提条件，如果不使用key值判断，会默认更新所有节点，而Vue中组件的更新过程也是极其复杂的，所以会造成一些不必要性能的成本；所以key可以更高效的帮助我们判断节点的可复用性。</p></blockquote><h3 id="为什么不能使用index作为key" tabindex="-1">为什么不能使用index作为key？ <a class="header-anchor" href="#为什么不能使用index作为key" aria-label="Permalink to &quot;为什么不能使用index作为key？&quot;">​</a></h3><p>很简单，来看个例子：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;ul&gt;                      &lt;ul&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li key= 0 &gt;a&lt;/li&gt;        &lt;li key= 0 &gt;new&lt;/li&gt;  // 新增</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li key= 1 &gt;b&lt;/li&gt;        &lt;li key= 1 &gt;a&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    &lt;li key= 2 &gt;c&lt;/li&gt;        &lt;li key= 2 &gt;b&lt;/li&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">                              &lt;li key= 3 &gt;c&lt;/li&gt;&lt;/ul&gt;                                               </span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;/ul&gt;</span></span></code></pre></div><p>按理来说，我们应该会复用里面的 <strong>a、b、c</strong> 三个节点对吧；</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmcPmUUqq816JOGvfQ6qhfa9Hb9PDiawhn7vD1frB4FFsTaz3deCnvl3YA/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>看这个例子，我们直接<strong>unshift()</strong> 插入列表一个新元素，<strong>这时候index发生了变化！！即key也会发生变化！！</strong></p><p>但是我们知道：按照Vue中的比较思路，这样的话，我们就无法复用哪些本来可以复用的节点，导致该节点被重新渲染一次，造成vue组件内一些列的更新，如果列表一旦很大，<strong>开销成本巨大</strong>！</p><p><img src="https://mmbiz.qpic.cn/mmbiz_png/ulhWEWict5AjY4lib33zqb3VG9Ul8nAjmczroLictUVaVMhwSjH2rv588yiaQHnZ6D6o5gLO44Bbwhxib6a6tlaR8tQ/640?wx_fmt=png&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>只要此时你的列表是一个<strong>动态的列表</strong>：而且使用了<strong>index作为key值</strong>，当你<strong>新增</strong>或者<strong>删除</strong>列表时候，key的排序总是以0、1、2、3...去排序的，而这样也会导致列表元素的<strong>key值在不断变化</strong>；导致 Vue 不能准确的找到可复用的节点，而是去<strong>直接做了patch操作</strong>，造成很多额外的工作。</p><h3 id="解决办法-唯一值" tabindex="-1">解决办法--唯一值 <a class="header-anchor" href="#解决办法-唯一值" aria-label="Permalink to &quot;解决办法--唯一值&quot;">​</a></h3><p>这也是我们为什么要用一个唯一的值去作为列表的key值的原因了！所以我们一般可以用id/唯一值作为key，这是规范问题，所以大家以后再看到项目中有index作为key的情况，请让他去学习diff算法吧哈哈哈！</p><p>所以在学习了diff之后要警示我们：</p><ul><li><p>1、key值要选择一个<strong>唯一值</strong>，通常用<strong>id来做key</strong></p></li><li><p>2、不要做一些无谓的dom结构修改或者跨层级去操作一些dom</p></li></ul><h1 id="vue-30道面试题" tabindex="-1">Vue 30道面试题 <a class="header-anchor" href="#vue-30道面试题" aria-label="Permalink to &quot;Vue 30道面试题&quot;">​</a></h1><h2 id="了解过-用过-react或者angular吗-他们有什么区别" tabindex="-1">了解过（用过）react或者angular吗，他们有什么区别？ <a class="header-anchor" href="#了解过-用过-react或者angular吗-他们有什么区别" aria-label="Permalink to &quot;了解过（用过）react或者angular吗，他们有什么区别？&quot;">​</a></h2><p><strong>答案</strong></p><p><code>Vue</code> 借鉴了<code> angular</code> 的模板和数据绑定技术，又借鉴了<code> react</code> 的组件化和虚拟<code> DOM</code> 技术。</p><p>😶对<code> Vue</code> 比较熟一些是吧~（这里只说<code> Vue</code> 假设你就只熟练<code> Vue</code> ）</p><h2 id="那首先谈谈你对vue的理解吧" tabindex="-1">那首先谈谈你对Vue的理解吧？ <a class="header-anchor" href="#那首先谈谈你对vue的理解吧" aria-label="Permalink to &quot;那首先谈谈你对Vue的理解吧？&quot;">​</a></h2><p><strong>答案</strong></p><p><strong>官网介绍：</strong> <strong>cn.vuejs.org/index.html</strong>**[1]**</p><p><strong>关键点：</strong> 渐进式<code> JavaScript</code> 框架、核心库加插件、动态创建用户界面（异步获取后台数据，数据展示在界面）</p><p><strong>特点：</strong> <code>MVVM</code> 模式；代码简洁体积小，运行效率高，适合移动PC端开发；本身只关注<code> UI</code> （和<code> react</code> 相似），可以轻松引入 <code>Vue</code> 插件或其他的第三方库进行开发。</p><p>🌸思考一下自己所说的那些点，自己都非常清楚明白吗？</p><p>下面呢我就根据你对<code> vue</code> 的理解，接着谈谈：</p><h2 id="你刚刚说到了mvvm-能详细说说吗" tabindex="-1">你刚刚说到了MVVM，能详细说说吗？ <a class="header-anchor" href="#你刚刚说到了mvvm-能详细说说吗" aria-label="Permalink to &quot;你刚刚说到了MVVM，能详细说说吗？&quot;">​</a></h2><p><strong>答案</strong></p><p>全称：<code> Model-View-ViewModel</code> ，<code> Model</code> 表示数据模型层。<code> view</code> 表示视图层，<code> ViewModel</code> 是<code> View</code> 和<code> Model</code> 层的桥梁，数据绑定到<code> viewModel</code> 层并自动渲染到页面中，视图变化通知<code> viewModel</code> 层更新数据。</p><p>😶摸底差不多，问基础了，响应式数据得知道吧，问一问</p><h2 id="vue是如何实现响应式数据的呢-响应式数据原理-❗" tabindex="-1">vue是如何实现响应式数据的呢？（响应式数据原理）❗ <a class="header-anchor" href="#vue是如何实现响应式数据的呢-响应式数据原理-❗" aria-label="Permalink to &quot;vue是如何实现响应式数据的呢？（响应式数据原理）❗&quot;">​</a></h2><p><strong>答案</strong></p><p>Vue2：<code> Object.defineProperty</code> 重新定义<code> data</code> 中所有的属性，<code> Object.defineProperty</code> 可以使数据的获取与设置增加一个拦截的功能，拦截属性的获取，进行依赖收集。拦截属性的更新操作，进行通知。</p><p>具体的过程：首先Vue使用 <code>initData</code> 初始化用户传入的参数，然后使用 <code>new Observer</code> 对数据进行观测，如果数据是一个对象类型就会调用<code> this.walk（value）</code> 对对象进行处理，内部使用 <code>defineeReactive</code> 循环对象属性定义响应式变化，核心就是使用<code> Object.defineProperty</code> 重新定义数据。</p><p><img src="https://mmbiz.qpic.cn/mmbiz/TZL4BdZpLdjE6lcuCbD0gecU0gT0dGVDEowAl1GMyK09OZuib918zDwg0BqcLFM9DeXOUnqOibCcWQWzoWEW7j3A/640?wx_fmt=other&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p><p>🌸刚刚如果你说了对象的检测，然后又没说清楚数组的处理的话，我就会问下面这个问题</p><h2 id="那vue中是如何检测数组变化的呢" tabindex="-1">那vue中是如何检测数组变化的呢？ <a class="header-anchor" href="#那vue中是如何检测数组变化的呢" aria-label="Permalink to &quot;那vue中是如何检测数组变化的呢？&quot;">​</a></h2><p><strong>答案</strong></p><p>数组就是使用<code> object.defineProperty</code> 重新定义数组的每一项，那能引起数组变化的方法我们都是知道的，<code> pop</code> 、<code> push</code> 、<code> shift</code> 、<code> unshift</code> 、<code> splice</code> 、<code> sort</code> 、<code> reverse</code> 这七种，只要这些方法执行改了数组内容，我就更新内容就好了，是不是很好理解。</p><ol><li>是用来函数劫持的方式，重写了数组方法，具体呢就是更改了数组的原型，更改成自己的，用户调数组的一些方法的时候，走的就是自己的方法，然后通知视图去更新。</li><li>数组里每一项可能是对象，那么我就是会对数组的每一项进行观测，（且只有数组里的对象才能进行观测，观测过的也不会进行观测）</li></ol><p>vue3：改用<code> proxy</code> ，可直接监听对象数组的变化。</p><h2 id="那你说说vue的事件绑定原理吧" tabindex="-1">那你说说Vue的事件绑定原理吧 <a class="header-anchor" href="#那你说说vue的事件绑定原理吧" aria-label="Permalink to &quot;那你说说Vue的事件绑定原理吧&quot;">​</a></h2><p><strong>答案</strong></p><ul><li>原生<code> DOM</code> 的绑定：Vue在创建真实DOM时会调用<code> createElm</code> ，默认会调用<code> invokeCreateHooks</code> 。会遍历当前平台下相对的属性处理代码，其中就有<code> updateDOMListeners</code> 方法，内部会传入<code> add（）</code> 方法</li><li>组件绑定事件，原生事件，自定义事件；组件绑定之间是通过Vue中自定义的<code> $on</code> 方法实现的。 （可以理解为：组件的<code> nativeOnOn</code> 等价于 普通元素on 组件的on会单独处理）</li></ul><h2 id="v-model中的实现原理及如何自定义v-model-❗" tabindex="-1">v-model中的实现原理及如何自定义v-model ❗ <a class="header-anchor" href="#v-model中的实现原理及如何自定义v-model-❗" aria-label="Permalink to &quot;v-model中的实现原理及如何自定义v-model ❗&quot;">​</a></h2><p><strong>答案</strong></p><p><code>v-model</code> 可以看成是<code> value+input</code> 方法的语法糖（组件）。原生的<code> v-model</code> ，会根据标签的不同生成不同的事件与属性。解析一个指令来。</p><p>自定义：自己写<code> model</code> 属性，里面放上<code> prop</code> 和<code> event</code></p><p>👍还行哟~知道响应式数据和数据绑定问完了，接着问问渲染呗：</p><h2 id="为什么vue采用异步渲染呢" tabindex="-1">为什么Vue采用异步渲染呢？ <a class="header-anchor" href="#为什么vue采用异步渲染呢" aria-label="Permalink to &quot;为什么Vue采用异步渲染呢？&quot;">​</a></h2><p><strong>答案</strong></p><p><code>Vue</code> 是组件级更新，如果不采用异步更新，那么每次更新数据都会对当前组件进行重新渲染，所以为了性能，<code> Vue</code> 会在本轮数据更新后，在异步更新视图。核心思想<code> nextTick</code> 。</p><p><code>dep.notify（）</code> 通知 watcher进行更新，<code> subs[i].update</code> 依次调用 watcher 的<code> update</code> ，<code> queueWatcher</code> 将watcher 去重放入队列， nextTick（<code> flushSchedulerQueue</code> ）在下一tick中刷新watcher队列（异步）。</p><p>🌸接着追问，要是你nextTick都能讲得很清楚的话那基本你是明白了。</p><h2 id="了解nexttick吗" tabindex="-1">了解nextTick吗？ <a class="header-anchor" href="#了解nexttick吗" aria-label="Permalink to &quot;了解nextTick吗？&quot;">​</a></h2><p><strong>答案</strong></p><p>异步方法，异步渲染最后一步，与JS事件循环联系紧密。主要使用了宏任务微任务（<code>setTimeout</code>、<code>promise</code>那些），定义了一个异步方法，多次调用<code>nextTick</code>会将方法存入队列，通过异步方法清空当前队列。</p><p>可以的可以的，先问你个生命周期，我再想想怎么难住你😄</p><h2 id="说说vue的生命周期吧-❗" tabindex="-1">说说Vue的生命周期吧 ❗ <a class="header-anchor" href="#说说vue的生命周期吧-❗" aria-label="Permalink to &quot;说说Vue的生命周期吧 ❗&quot;">​</a></h2><p><strong>答案</strong></p><p><strong>什么时候被调用？</strong></p><ul><li>beforeCreate ：实例初始化之后，数据观测之前调用</li><li>created：实例创建万之后调用。实例完成：数据观测、属性和方法的运算、<code> watch/event</code> 事件回调。无<code> $el</code> .</li><li>beforeMount：在挂载之前调用，相关<code> render</code> 函数首次被调用</li><li>mounted：了被新创建的<code>vm.$el</code>替换，并挂载到实例上去之后调用改钩子。</li><li>beforeUpdate：数据更新前调用，发生在虚拟DOM重新渲染和打补丁，在这之后会调用改钩子。</li><li>updated：由于数据更改导致的虚拟DOM重新渲染和打补丁，在这之后会调用改钩子。</li><li>beforeDestroy：实例销毁前调用，实例仍然可用。</li><li>destroyed：实例销毁之后调用，调用后，Vue实例指示的所有东西都会解绑，所有事件监听器和所有子实例都会被移除</li></ul><p><strong>每个生命周期内部可以做什么？</strong></p><ul><li>created：实例已经创建完成，因为他是最早触发的，所以可以进行一些数据、资源的请求。</li><li>mounted：实例已经挂载完成，可以进行一些DOM操作。</li><li>beforeUpdate：可以在这个钩子中进一步的更改状态，不会触发重渲染。</li><li>updated：可以执行依赖于DOM的操作，但是要避免更改状态，可能会导致更新无线循环。</li><li>destroyed：可以执行一些优化操作，清空计时器，解除绑定事件。</li></ul><p><strong>ajax放在哪个生命周期？</strong> ：一般放在<code> mounted</code> 中，保证逻辑统一性，因为生命周期是同步执行的，<code> ajax</code> 是异步执行的。单数服务端渲染<code> ssr</code> 同一放在<code> created</code> 中，因为服务端渲染不支持<code> mounted</code> 方法。 <strong>什么时候使用beforeDestroy？</strong> ：当前页面使用<code> $on</code> ，需要解绑事件。清楚定时器。解除事件绑定，<code> scroll mousemove</code> 。</p><h2 id="父子组件生命周期调用顺序-简单" tabindex="-1">父子组件生命周期调用顺序（简单） <a class="header-anchor" href="#父子组件生命周期调用顺序-简单" aria-label="Permalink to &quot;父子组件生命周期调用顺序（简单）&quot;">​</a></h2><p><strong>答案</strong></p><p>渲染顺序：先父后子，完成顺序：先子后父</p><p>更新顺序：父更新导致子更新，子更新完成后父</p><p>销毁顺序：先父后子，完成顺序：先子后父</p><h2 id="vue组件通信-❗" tabindex="-1">Vue组件通信 ❗ <a class="header-anchor" href="#vue组件通信-❗" aria-label="Permalink to &quot;Vue组件通信 ❗&quot;">​</a></h2><p><strong>答案</strong></p><ul><li>父子间通信:父亲提供数据通过属性<code> props</code>传给儿子；儿子通过<code> $on</code> 绑父亲的事件，再通过<code> $emit</code> 触发自己的事件（发布订阅）</li><li>利用父子关系<code> $parent</code> 、<code> $children</code> ， 获取父子组件实例的方法。</li><li>父组件提供数据，子组件注入。<code> provide</code> 、<code> inject</code> ，插件用得多。</li><li><code>ref</code> 获取组件实例，调用组件的属性、方法</li><li>跨组件通信<code> Event Bus</code> （Vue.prototype.bus=newVue）其实基于bus = new Vue）其实基于bus=newVue）其实基于on与$emit</li><li><code>vuex</code> 状态管理实现通信</li></ul><h2 id="vuex-工作原理" tabindex="-1">Vuex 工作原理 <a class="header-anchor" href="#vuex-工作原理" aria-label="Permalink to &quot;Vuex 工作原理&quot;">​</a></h2><p><strong>答案</strong></p><p>官网：<strong>vuex.vuejs.org/zh/****[2]</strong></p><ul><li><p>Vuex 是一个专为 Vue.js 应用程序开发的<strong>状态管理模式</strong>。</p></li><li><p>状态自管理应用包含以下几个部分：</p><p><img src="https://mmbiz.qpic.cn/mmbiz/TZL4BdZpLdjE6lcuCbD0gecU0gT0dGVDBC9UJgU1wCr3TRPt0eSpRjoonfPypryxJgUyLNiaSpCiaLkepBFxMD3A/640?wx_fmt=other&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></p></li><li><ul><li>state，驱动应用的数据源；</li><li>view，以声明方式将 state 映射到视图；</li><li>actions，响应在 view 上的用户输入导致的状态变化。下图单向数据流示意图：</li></ul></li><li><p>vuex，多组件共享状态，因-单向数据流简洁性很容易被破坏：</p></li><li><ul><li>多个视图依赖于同一状态。</li><li>来自不同视图的行为需要变更同一状态。<img src="https://mmbiz.qpic.cn/mmbiz/TZL4BdZpLdjE6lcuCbD0gecU0gT0dGVDaDvaCDCO5rtShrG74PcibW91gxYIfBYkQ6tc7Ich806hAr85hDqibFEQ/640?wx_fmt=other&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片"></li></ul></li></ul><p>问虚拟<code> DOM</code> 吧，看你能不能讲清楚从真实<code> DOM</code> 到虚拟<code> DOM</code> ，再和我说说<code> diff</code></p><h2 id="如何从真实dom到虚拟dom" tabindex="-1">如何从真实DOM到虚拟DOM <a class="header-anchor" href="#如何从真实dom到虚拟dom" aria-label="Permalink to &quot;如何从真实DOM到虚拟DOM&quot;">​</a></h2><p><strong>答案</strong></p><p>涉及到Vue中的模板编译原理，主要过程：</p><ol><li>将模板转换成<code> ast</code> 树，<code> ast</code> 用对象来描述真实的JS语法（将真实DOM转换成虚拟DOM）</li><li>优化树</li><li>将<code> ast</code> 树生成代码</li></ol><h2 id="用vnode来描述一个dom结构" tabindex="-1">用VNode来描述一个DOM结构 <a class="header-anchor" href="#用vnode来描述一个dom结构" aria-label="Permalink to &quot;用VNode来描述一个DOM结构&quot;">​</a></h2><p><strong>答案</strong></p><p>虚拟节点就是用一个对象来描述一个真实的DOM元素。首先将<code> template</code> （真实DOM）先转成<code> ast</code> ，<code> ast</code> 树通过<code> codegen</code> 生成<code> render</code> 函数，<code> render</code> 函数里的<code> _c</code> 方法将它转为虚拟dom</p><h2 id="diff算法" tabindex="-1">diff算法 <a class="header-anchor" href="#diff算法" aria-label="Permalink to &quot;diff算法&quot;">​</a></h2><p><strong>答案</strong></p><p><strong>时间复杂度：</strong> 个树的完全<code> diff</code> 算法是一个时间复杂度为<code> O(n*3）</code> ，vue进行优化转化成<code> O(n)</code> 。</p><p><strong>理解：</strong></p><ul><li><p>最小量更新，<code> key</code> 很重要。这个可以是这个节点的唯一标识，告诉<code> diff</code> 算法，在更改前后它们是同一个DOM节点</p></li><li><ul><li>扩展<code> v-for</code> 为什么要有<code> key</code> ，没有<code> key</code> 会暴力复用，举例子的话随便说一个比如移动节点或者增加节点（修改DOM），加<code> key</code> 只会移动减少操作DOM。</li></ul></li><li><p>只有是同一个虚拟节点才会进行精细化比较，否则就是暴力删除旧的，插入新的。</p></li><li><p>只进行同层比较，不会进行跨层比较。</p></li></ul><p><strong>diff算法的优化策略</strong>：四种命中查找，四个指针</p><ol><li>旧前与新前（先比开头，后插入和删除节点的这种情况）</li><li>旧后与新后（比结尾，前插入或删除的情况）</li><li>旧前与新后（头与尾比，此种发生了，涉及移动节点，那么新前指向的节点，移动到旧后之后）</li><li>旧后与新前（尾与头比，此种发生了，涉及移动节点，那么新前指向的节点，移动到旧前之前）</li></ol><p>--- 问完上面这些如果都能很清楚的话，基本O了 ---</p><p>以下的这些简单的概念，你肯定也是没有问题的啦😉</p><h2 id="computed-watch-和-method" tabindex="-1">Computed watch 和 method <a class="header-anchor" href="#computed-watch-和-method" aria-label="Permalink to &quot;Computed watch 和 method&quot;">​</a></h2><p><strong>答案</strong></p><p><strong>computed</strong>：默认<code>computed</code>也是一个<code>watcher</code>具备缓存，只有当依赖的数据变化时才会计算, 当数据没有变化时, 它会读取缓存数据。如果一个数据依赖于其他数据，使用<code> computed</code></p><p><strong>watch</strong>：每次都需要执行函数。 <code>watch</code> 更适用于数据变化时的异步操作。如果需要在某个数据变化时做一些事情，使用watch。</p><p><strong>method</strong>：只要把方法用到模板上了,每次一变化就会重新渲染视图，性能开销大</p><h2 id="v-if-和-v-show-区别" tabindex="-1">v-if 和 v-show 区别 <a class="header-anchor" href="#v-if-和-v-show-区别" aria-label="Permalink to &quot;v-if 和 v-show 区别&quot;">​</a></h2><p><strong>答案</strong></p><ul><li><code>v-if</code> 如果条件不成立不会渲染当前指令所在节点的DOM元素</li><li><code>v-show</code> 只是切换当前DOM的显示与隐藏</li></ul><h2 id="v-for和v-if为什么不能连用" tabindex="-1">v-for和v-if为什么不能连用 <a class="header-anchor" href="#v-for和v-if为什么不能连用" aria-label="Permalink to &quot;v-for和v-if为什么不能连用&quot;">​</a></h2><p><strong>答案</strong></p><p><code>v-for</code> 会比 <code>v-if</code> 的优先级更高，连用的话会把<code> v-if</code> 的每个元素都添加一下，造成性能问题。</p><h2 id="v-html-会导致哪些问题-简单" tabindex="-1">v-html 会导致哪些问题（简单） <a class="header-anchor" href="#v-html-会导致哪些问题-简单" aria-label="Permalink to &quot;v-html 会导致哪些问题（简单）&quot;">​</a></h2><p><strong>答案</strong></p><ul><li><code>XSS</code> 攻击</li><li><code>v-html</code> 会替换标签内部的元素</li></ul><h2 id="描述组件渲染和更新过程" tabindex="-1">描述组件渲染和更新过程 <a class="header-anchor" href="#描述组件渲染和更新过程" aria-label="Permalink to &quot;描述组件渲染和更新过程&quot;">​</a></h2><p><strong>答案</strong></p><p>渲染组件时，会通过<code> vue.extend()</code> 方法构建子组件的构造函数，并进行实例化。最终手动调用<code> $mount()</code> 进行挂载。更新组件时会进行<code> patchVnode</code> 流程，核心就是<code> diff</code> 算法。</p><h2 id="组件中的data为什么是函数" tabindex="-1">组件中的data为什么是函数 <a class="header-anchor" href="#组件中的data为什么是函数" aria-label="Permalink to &quot;组件中的data为什么是函数&quot;">​</a></h2><p><strong>答案</strong></p><p>避免组件中的数据互相影响。同一个组件被复用多次会创建多个实例，如果<code> data</code> 是一个对象的话，这些实例用的是同一个构造函数。为了保证组件的数据独立，要求每个组件都必须通过<code> data</code> 函数返回一个对象作为组件的状态。</p><h2 id="为什么要使用异步组件" tabindex="-1">为什么要使用异步组件？ <a class="header-anchor" href="#为什么要使用异步组件" aria-label="Permalink to &quot;为什么要使用异步组件？&quot;">​</a></h2><p><strong>答案</strong></p><ol><li>节省打包出的结果，异步组件分开打包，采用jsonp的方式进行加载，有效解决文件过大的问题。</li><li>核心就是包组件定义变成一个函数，依赖<code> import（）</code> 语法，可以实现文件的分割加载。 详细的看官方文档：<strong>cn.vuejs.org/v2/guide/co…****[3]</strong></li></ol><h2 id="action-与-mutation-的区别" tabindex="-1">action 与 mutation 的区别 <a class="header-anchor" href="#action-与-mutation-的区别" aria-label="Permalink to &quot;action 与 mutation 的区别&quot;">​</a></h2><p><strong>答案</strong></p><ul><li><code>mutation</code> 是同步更新，<code> $watch</code> 严格模式下会报错</li><li><code>action</code> 是异步操作，可以获取数据后调用<code> mutation</code> 提交最终数据</li></ul><h2 id="插槽与作用域插槽的区别" tabindex="-1">插槽与作用域插槽的区别 <a class="header-anchor" href="#插槽与作用域插槽的区别" aria-label="Permalink to &quot;插槽与作用域插槽的区别&quot;">​</a></h2><h3 id="插槽-1" tabindex="-1">插槽 <a class="header-anchor" href="#插槽-1" aria-label="Permalink to &quot;插槽&quot;">​</a></h3><p><strong>答案</strong></p><ul><li>创建组件虚拟节点时，会将组件儿子的虚拟节点保存起来。当初始化组件时，通过插槽属性将儿子进行分类<code> {a:[vnode],b[vnode]}</code></li><li>渲染组件时会拿对应的<code> slot</code> 属性的节点进行替换操作。（插槽的作用域为父组件）</li></ul><h3 id="作用域插槽" tabindex="-1">作用域插槽 <a class="header-anchor" href="#作用域插槽" aria-label="Permalink to &quot;作用域插槽&quot;">​</a></h3><p><strong>答案</strong></p><ul><li>作用域插槽在解析的时候不会作为组件的孩子节点。会解析成函数，当子组件渲染时，会调用此函数进行渲染。</li><li>普通插槽渲染的作用域是父组件，作用域插槽的渲染作用域是当前子组件。</li></ul><h2 id="vue中相同逻辑如何抽离" tabindex="-1">vue中相同逻辑如何抽离 <a class="header-anchor" href="#vue中相同逻辑如何抽离" aria-label="Permalink to &quot;vue中相同逻辑如何抽离&quot;">​</a></h2><p><strong>答案</strong></p><p>其实就是考察<code> vue.mixin</code> 用法，给组件每个生命周期，函数都混入一些公共逻辑。</p><h2 id="谈谈对keep-alive的了解" tabindex="-1">谈谈对keep-alive的了解 <a class="header-anchor" href="#谈谈对keep-alive的了解" aria-label="Permalink to &quot;谈谈对keep-alive的了解&quot;">​</a></h2><p><strong>答案</strong></p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">keep-alive` 可以实现组件的缓存，当组件切换时不会对当前组件进行卸载。常用的2个属性` include/exclude` ，2个生命周期` activated` ，` deactivated</span></span></code></pre></div><h2 id="vue性能优化" tabindex="-1">Vue性能优化 <a class="header-anchor" href="#vue性能优化" aria-label="Permalink to &quot;Vue性能优化&quot;">​</a></h2><p><strong>答案</strong></p><p><strong>编码优化</strong>：</p><ul><li>事件代理</li><li><code>keep-alive</code></li><li>拆分组件</li><li><code>key</code> 保证唯一性</li><li>路由懒加载、异步组件</li><li>防抖节流</li></ul><p><strong>Vue加载性能优化</strong></p><ul><li>第三方模块按需导入（<code> babel-plugin-component</code> ）</li><li>图片懒加载</li></ul><p><strong>用户体验</strong></p><ul><li><code>app-skeleton</code> 骨架屏</li><li><code>shellap</code> p壳</li><li><code>pwa</code></li></ul><p><strong>SEO优化</strong></p><ul><li>预渲染</li></ul><h1 id="vite-3-vue-3-前端工程化项目" tabindex="-1">Vite 3 + Vue 3 前端工程化项目 <a class="header-anchor" href="#vite-3-vue-3-前端工程化项目" aria-label="Permalink to &quot;Vite 3 + Vue 3 前端工程化项目&quot;">​</a></h1><p><a href="https://mp.weixin.qq.com/s?__biz=MzI4OTY2MzE0OA==&amp;mid=2247503103&amp;idx=1&amp;sn=840dd1f04c4ba22cc7a9e8f452879ddb&amp;chksm=ec293d3cdb5eb42a26dd54dc63711453720706cc38f94af8e6eec2db7b1ccb1b750d0787eff8&amp;mpshare=1&amp;scene=23&amp;srcid=1122NdWvD8xOx0v58sgM2TQ2&amp;sharer_sharetime=1669098891772&amp;sharer_shareid=29b8a04db1dbd975e3bf4e9f47e7ac67#rd" target="_blank" rel="noreferrer">从 0 搭建 Vite 3 + Vue 3 前端工程化项目 (qq.com)</a></p><h1 id="前言" tabindex="-1">前言 <a class="header-anchor" href="#前言" aria-label="Permalink to &quot;前言&quot;">​</a></h1><p>Vue 3 正式版已经发布有一段时间了，随着 Vite 脚手架注定成为下一代前端工具链，许多用户都想基于 Vite 来构建 Vue 项目，如果想基于 Vite 构建 Vue 3 项目，社区模板完全满足您的需求，如果想构建 Vite 3 + Vue 3 + JavaScript 项目，那社区模板不太能满足您的需求，因为社区模板提供 Vue 3 项目几乎是基于 Vite 2 + TypeScript 构建，对于不熟悉 TypeScript 语言的用户不是很友好，因此接下来从 0 开始手把手带大家搭建一套规范的 Vite 3 + Vue 3 + JavaScript 前端工程化项目环境。</p><p>本文章篇幅较长，从以下几个方面展开：</p><ul><li>基础搭建</li><li>代码规范</li><li>提交规范</li><li>自动部署</li></ul><blockquote><p>本项目完整代码托管在 <strong>GitHub 仓库</strong>[1]，欢迎点亮小星星 🌟🌟</p></blockquote><h1 id="技术栈" tabindex="-1">技术栈 <a class="header-anchor" href="#技术栈" aria-label="Permalink to &quot;技术栈&quot;">​</a></h1><ul><li>⚡️ <strong>Vite 3</strong>[2] - 构建工具（就是快！）</li><li>🖖 <strong>Vue 3</strong>[3] - 渐进式 JavaScript 框架</li><li>🚦 <strong>Vue Router</strong>[4] - 官方路由管理器</li><li>📦 <strong>Pinia</strong>[5] - 值得你喜欢的 Vue Store</li><li>💻 <strong>TDesign</strong>[6] - TDesign 适配桌面端的组件库</li><li>🎨 <strong>Less</strong>[7] - CSS 预处理器</li><li>🔗 <strong>Axios</strong>[8] - 一个基于 promise 的网络请求库，可以用于浏览器和 node.js</li><li>🧰 <strong>Husky</strong>[9] + <strong>Lint-Staged</strong>[10] - Git Hook 工具</li><li>🛡️ <strong>EditorConfig</strong>[11] + <strong>ESLint</strong>[12] + <strong>Prettier</strong>[13] + <strong>Stylelint</strong>[14] - 代码规范</li><li>🔨 <strong>Commitizen</strong>[15] + <strong>Commitlint</strong>[16] - 提交规范</li><li>💡 <strong>GitHub Actions</strong>[17] - 自动部署</li></ul><h1 id="基础搭建" tabindex="-1">基础搭建 <a class="header-anchor" href="#基础搭建" aria-label="Permalink to &quot;基础搭建&quot;">​</a></h1><h2 id="构建项目雏形" tabindex="-1">构建项目雏形 <a class="header-anchor" href="#构建项目雏形" aria-label="Permalink to &quot;构建项目雏形&quot;">​</a></h2><p>确保你安装了最新版本的 <strong>Node.js</strong>，然后在命令行中运行以下命令：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># npm 6.x</span></span>
<span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">create</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite@latest</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite-vue-js-template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vue</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># npm 7+, extra double-dash is needed:(本次使用它进行创建)</span></span>
<span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">create</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite@latest</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite-vue-js-template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vue</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># yarn</span></span>
<span class="line"><span style="color:#FFCB6B;">yarn</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">create</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite-vue-js-template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vue</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># pnpm</span></span>
<span class="line"><span style="color:#FFCB6B;">pnpm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">create</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vite-vue-js-template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">--template</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vue</span></span></code></pre></div><p>这一指令将会安装并执行 <strong>create-vite</strong>，它是一个基本模板快速启动项目工具。</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211221956927.png" alt="image-20221122195654856" style="zoom:80%;"><p>在项目被创建后，通过以下步骤安装依赖并启动开发服务器：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 打开项目</span></span>
<span class="line"><span style="color:#82AAFF;">cd</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&lt;</span><span style="color:#C3E88D;">your-project-nam</span><span style="color:#A6ACCD;">e</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 安装依赖</span></span>
<span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">install</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 启动项目</span></span>
<span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">run</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">dev</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211221958917.png" alt="image-20221122195805864" style="zoom:67%;"><h2 id="vite-基础配置" tabindex="-1">Vite 基础配置 <a class="header-anchor" href="#vite-基础配置" aria-label="Permalink to &quot;Vite 基础配置&quot;">​</a></h2><p>Vite 配置文件 <code>vite.config.js</code> 位于项目根目录下，项目启动时会自动读取。</p><p>本项目针对公共基础路径、自定义路径别名、服务器选项、构建选项等做了如下基础配置：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">defineConfig</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vite</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">resolve</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">path</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> vue </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">@vitejs/plugin-vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">defineConfig</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">base</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">plugins</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#82AAFF;">vue</span><span style="color:#A6ACCD;">()</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">resolve</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">alias</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">@</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">resolve</span><span style="color:#A6ACCD;">(__dirname</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./src</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">) </span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">server</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 是否开启 https</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">https</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 端口号</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">port</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">3000</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 监听所有地址</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">host</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">0.0.0.0</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 服务启动时是否自动打开浏览器</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">open</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 允许跨域</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">cors</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 自定义代理规则</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">proxy</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">build</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 设置最终构建的浏览器兼容目标</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">target</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">es2015</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 构建后是否生成 source map 文件</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">sourcemap</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">//  chunk 大小警告的限制（以 kbs 为单位）</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">chunkSizeWarningLimit</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2000</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">      </span><span style="color:#676E95;font-style:italic;">// 启用/禁用 gzip 压缩大小报告</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">reportCompressedSize</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><p>关于 Vite 更多配置项及用法，请查看 Vite 官网 <strong>vitejs.dev/config/</strong></p><h2 id="规范目录结构" tabindex="-1">规范目录结构 <a class="header-anchor" href="#规范目录结构" aria-label="Permalink to &quot;规范目录结构&quot;">​</a></h2><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">├── dist</span><span style="color:#89DDFF;">/</span></span>
<span class="line"><span style="color:#A6ACCD;">└── src</span><span style="color:#89DDFF;">/</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── api</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                       </span><span style="color:#676E95;font-style:italic;">// 接口请求目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── assets</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                    </span><span style="color:#676E95;font-style:italic;">// 静态资源目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── common</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                    </span><span style="color:#676E95;font-style:italic;">// 通用类库目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── components</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                </span><span style="color:#676E95;font-style:italic;">// 公共组件目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── router</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                    </span><span style="color:#676E95;font-style:italic;">// 路由配置目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── store</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                     </span><span style="color:#676E95;font-style:italic;">// 状态管理目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── style</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                     </span><span style="color:#676E95;font-style:italic;">// 通用样式目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── utils</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                     </span><span style="color:#676E95;font-style:italic;">// 工具函数目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── views</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                     </span><span style="color:#676E95;font-style:italic;">// 页面组件目录</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── App</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">vue</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── main</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">js</span></span>
<span class="line"><span style="color:#A6ACCD;">├── tests</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">                         </span><span style="color:#676E95;font-style:italic;">// 单元测试目录</span></span>
<span class="line"><span style="color:#A6ACCD;">├── index</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">html</span></span>
<span class="line"><span style="color:#A6ACCD;">├── jsconfig</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">json                  </span><span style="color:#676E95;font-style:italic;">// JavaScript 配置文件</span></span>
<span class="line"><span style="color:#A6ACCD;">├── vite</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">config</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">js                 </span><span style="color:#676E95;font-style:italic;">// Vite 配置文件</span></span>
<span class="line"><span style="color:#A6ACCD;">└── package</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">json</span></span></code></pre></div><h2 id="集成-vue-router4-路由" tabindex="-1">集成 Vue Router4 路由 <a class="header-anchor" href="#集成-vue-router4-路由" aria-label="Permalink to &quot;集成 Vue Router4 路由&quot;">​</a></h2><h3 id="安装依赖" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">i</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">vue-router@4</span></span></code></pre></div><h3 id="创建基本组件" tabindex="-1">创建基本组件 <a class="header-anchor" href="#创建基本组件" aria-label="Permalink to &quot;创建基本组件&quot;">​</a></h3><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211222018730.png" alt="image-20221122201819597" style="zoom:80%;"><div class="language-vue"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">AboutView</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><div class="language-vue"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">HomeView</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><h3 id="创建路由配置文件" tabindex="-1">创建路由配置文件 <a class="header-anchor" href="#创建路由配置文件" aria-label="Permalink to &quot;创建路由配置文件&quot;">​</a></h3><p>在 <code>src/router</code> 目录下新建 <code>index.js</code> 文件与 <code>modules</code> 文件夹</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">└── src</span><span style="color:#89DDFF;">/</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── router</span><span style="color:#89DDFF;">/</span></span>
<span class="line"><span style="color:#A6ACCD;">    	├── modules</span><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">// 路由模块</span></span>
<span class="line"><span style="color:#A6ACCD;">        ├── index</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">js  </span><span style="color:#676E95;font-style:italic;">// 路由配置文件</span></span></code></pre></div><ul><li></li></ul><p>关于路由表，建议根据功能的不同来拆分到 <code>modules</code> 文件夹中，好处是：方便后期维护,减少 Git 合并代码冲突可能性</p><blockquote><p>创建modules/base.js，写入如下路由内容</p></blockquote><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">path</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">/</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">redirect</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">/home</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">path</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">/home</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">name</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">home</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#82AAFF;">component</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">import</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./../../views/HomeView.vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">path</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">/about</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">name</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">about</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#82AAFF;">component</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">import</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./../../views/AboutView.vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">;</span></span></code></pre></div><p>在index.js中，进行路由基本配置</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">createRouter</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">createWebHistory</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue-router</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> baseRouters </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./modules/base.js</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> routes </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">...</span><span style="color:#A6ACCD;">baseRouters]</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> router </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">createRouter</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">history</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">createWebHistory</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">meta</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">env</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">BASE_URL)</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    routes</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">scrollBehavior</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">            el</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">#app</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">            top</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">            behavior</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">smooth</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;">};</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> router</span><span style="color:#89DDFF;">;</span></span></code></pre></div><p>根据路由配置的实际情况，需要在 <code>src</code> 下创建 <code>views</code> 目录，用来存储页面组件。</p><h3 id="挂载路由配置" tabindex="-1">挂载路由配置 <a class="header-anchor" href="#挂载路由配置" aria-label="Permalink to &quot;挂载路由配置&quot;">​</a></h3><p>在 <code>main.js</code> 文件中挂载路由配置</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">createApp</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./style.css</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> App </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./App.vue</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> router </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">./router</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> app </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">createApp</span><span style="color:#A6ACCD;">(App)</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 挂载路由模块，app.use用于挂载第三方插件模块</span></span>
<span class="line"><span style="color:#A6ACCD;">app</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(router)</span></span>
<span class="line"><span style="color:#A6ACCD;">app</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">mount</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">#app</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span></span></code></pre></div><h3 id="定义路由占位符" tabindex="-1">定义路由占位符 <a class="header-anchor" href="#定义路由占位符" aria-label="Permalink to &quot;定义路由占位符&quot;">​</a></h3><blockquote><p>App.vue中定义路由占位符</p></blockquote><div class="language-vue"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#676E95;font-style:italic;">&lt;!-- 路由的占位符 --&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">router-view</span><span style="color:#89DDFF;">&gt;&lt;/</span><span style="color:#F07178;">router-view</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><h3 id="访问测试" tabindex="-1">访问测试 <a class="header-anchor" href="#访问测试" aria-label="Permalink to &quot;访问测试&quot;">​</a></h3><p>访问路径：:3000/</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211222021480.png" alt="image-20221122202151365" style="zoom:80%;"><h2 id="集成-pinia-全局状态管理" tabindex="-1">集成 Pinia 全局状态管理 <a class="header-anchor" href="#集成-pinia-全局状态管理" aria-label="Permalink to &quot;集成 Pinia 全局状态管理&quot;">​</a></h2><h3 id="安装依赖-1" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-1" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i pinia</span></span></code></pre></div><h3 id="创建仓库配置文件" tabindex="-1">创建仓库配置文件 <a class="header-anchor" href="#创建仓库配置文件" aria-label="Permalink to &quot;创建仓库配置文件&quot;">​</a></h3><p>在 <code>src/store</code> 目录下新建 <code>index.js</code> 文件与 <code>modules</code> 文件夹</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211222035870.png" alt="image-20221122203554732" style="zoom:80%;"><p>开发中需要将不同功能所对应的状态，拆分到不同的 <code>modules</code>，好处如同路由模块一样。</p><p>counter.js</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">defineStore</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">pinia</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> useCounterStore </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">defineStore</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">counter</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#82AAFF;">state</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> (</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">count</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">actions</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">accumulate</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">            </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">count</span><span style="color:#89DDFF;">++;</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><p>index.js</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">createPinia</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">pinia</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> store </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">createPinia</span><span style="color:#A6ACCD;">()</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> store</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">*</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./modules/counter</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span></code></pre></div><h3 id="挂载-pinia-配置" tabindex="-1">挂载 Pinia 配置 <a class="header-anchor" href="#挂载-pinia-配置" aria-label="Permalink to &quot;挂载 Pinia 配置&quot;">​</a></h3><p>在 <code>main.js</code> 文件中挂载 <code>Vuex</code> 配置</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">createApp</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> App </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./App.vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> store </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./store</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> router </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./router</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#82AAFF;">createApp</span><span style="color:#A6ACCD;">(App)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(router)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(store)</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">mount</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">#app</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><h3 id="测试pinia" tabindex="-1">测试Pinia <a class="header-anchor" href="#测试pinia" aria-label="Permalink to &quot;测试Pinia&quot;">​</a></h3><p>在HomeView.vue页面</p><div class="language-vue"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">HomeView111</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">{{store}}</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;">useCounterStore</span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">./../store/index.js</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">name</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">HomeView</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">data</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      store</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">useCounterStore</span><span style="color:#F07178;">()</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211222038025.png" alt="image-20221122203839887" style="zoom:80%;"><h2 id="集成-tdesign-vue-next-组件库" tabindex="-1">集成 TDesign Vue Next 组件库 <a class="header-anchor" href="#集成-tdesign-vue-next-组件库" aria-label="Permalink to &quot;集成 TDesign Vue Next 组件库&quot;">​</a></h2><h3 id="安装依赖-2" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-2" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">i</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">tdesign-vue-next</span></span></code></pre></div><h3 id="基础使用" tabindex="-1">基础使用 <a class="header-anchor" href="#基础使用" aria-label="Permalink to &quot;基础使用&quot;">​</a></h3><blockquote><p>main.js中进行配置</p></blockquote><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">createApp</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> TDesign </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">tdesign-vue-next</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 引入组件库全局样式资源</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">tdesign-vue-next/es/style/index.css</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> app </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">createApp</span><span style="color:#A6ACCD;">(App)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">app</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(TDesign)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><h3 id="按需引入" tabindex="-1">按需引入 <a class="header-anchor" href="#按需引入" aria-label="Permalink to &quot;按需引入&quot;">​</a></h3><p>使用 <code>unplugin-vue-components</code> 和 <code>unplugin-auto-import</code> 来实现自动导入：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">install</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">unplugin-vue-components</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">unplugin-auto-import</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-D</span></span></code></pre></div><p>在 Vite 对应的配置文件 <code>vite.config.js</code> 添加上述插件：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> AutoImport </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">unplugin-auto-import/vite</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> Components </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">unplugin-vue-components/vite</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">TDesignResolver</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">unplugin-vue-components/resolvers</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">plugins</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#82AAFF;">AutoImport</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">resolvers</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#82AAFF;">TDesignResolver</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">library</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue-next</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#82AAFF;">Components</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">resolvers</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#82AAFF;">TDesignResolver</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">library</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue-next</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span></code></pre></div><h3 id="使用测试" tabindex="-1">使用测试 <a class="header-anchor" href="#使用测试" aria-label="Permalink to &quot;使用测试&quot;">​</a></h3><div class="language-vue"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">HomeView111</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#676E95;font-style:italic;">&lt;!--&lt;h3&gt;{{store}}&lt;/h3&gt;--&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">t-space</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">direction</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">vertical</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;"> align=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">center</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">block</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">theme</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">primary</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">variant</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">base</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">填充按钮</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">block</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">variant</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">outline</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">描边按钮</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">block</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">variant</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">dashed</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">虚框按钮</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">block</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">variant</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">text</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">文字按钮</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">t-button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">t-space</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211222046947.png" alt="image-20221122204643795" style="zoom:67%;"><h2 id="集成-axios-http-工具" tabindex="-1">集成 Axios HTTP 工具 <a class="header-anchor" href="#集成-axios-http-工具" aria-label="Permalink to &quot;集成 Axios HTTP 工具&quot;">​</a></h2><h3 id="安装依赖-3" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-3" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">i</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">axios</span></span></code></pre></div><h3 id="请求配置" tabindex="-1">请求配置 <a class="header-anchor" href="#请求配置" aria-label="Permalink to &quot;请求配置&quot;">​</a></h3><p>在 <code>utils</code> 目录下创建 <code>request.js</code> 文件，配置好适合自己业务的请求拦截和响应拦截：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">└── src</span><span style="color:#89DDFF;">/</span></span>
<span class="line"><span style="color:#A6ACCD;">	├── api  </span><span style="color:#676E95;font-style:italic;">// 接口</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── utils</span><span style="color:#89DDFF;">/</span></span>
<span class="line"><span style="color:#A6ACCD;">        ├── request</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">js  </span><span style="color:#676E95;font-style:italic;">// axios 请求库二次封装</span></span></code></pre></div><p>request.js中口进行如下配置</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> axios </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">axios</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 创建请求实例</span></span>
<span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> instance </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> axios</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">create</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">baseURL</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">:8080/</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 指定请求超时的毫秒数</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">timeout</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1000</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 表示跨域请求时是否需要使用凭证</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">withCredentials</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 前置拦截器（发起请求之前的拦截）</span></span>
<span class="line"><span style="color:#A6ACCD;">instance</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">interceptors</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">request</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">config</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         * 在这里一般会携带前台的参数发送给后台，比如下面这段代码：</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         * const token = getToken()</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         * if (token) {</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         *  config.headers.token = token</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         * }</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         */</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">config</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">error</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#FFCB6B;">Promise</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">reject</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">error</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 后置拦截器（获取到响应时的拦截）</span></span>
<span class="line"><span style="color:#A6ACCD;">instance</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">interceptors</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">response</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">use</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">response</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         * 根据你的项目实际情况来对 response 和 error 做处理</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         * 这里对 response 和 error 不做任何处理，直接返回</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">         */</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">response</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">error</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">=&gt;</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">response</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">error</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;font-style:italic;">if</span><span style="color:#F07178;"> (</span><span style="color:#A6ACCD;">response</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&amp;&amp;</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">response</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">data</span><span style="color:#F07178;">) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">            </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#FFCB6B;">Promise</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">reject</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">error</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">message</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">error</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">error</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">message</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#FFCB6B;">Promise</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">reject</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">error</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">// 导出常用函数</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">string</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">url</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">data</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">post</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">url</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">data</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{},</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{})</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">instance</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        method</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">post</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">url</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">data</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">params</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">string</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">url</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">get</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">url</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{})</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">instance</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        method</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">get</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">url</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">params</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">string</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">url</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">data</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">put</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">url</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">data</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{},</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{})</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">instance</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        method</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">put</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">url</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">params</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">data</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">string</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">url</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">object</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"> */</span></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">function</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">_delete</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">url</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">params</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{})</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">instance</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">        method</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">delete</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">url</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">        </span><span style="color:#A6ACCD;">params</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> instance</span><span style="color:#89DDFF;">;</span></span></code></pre></div><p>之后在 <code>api</code> 文件夹中以业务模型对接口进行拆分，举个例子，将所有跟用户相关接口封装在 <code>User</code> 类中，此类称作用户模型。在 <code>User</code> 类中比如有登录、注册、获取用户信息等方法，如果有业务逻辑变动，只需要修改相关方法即可。</p><blockquote><p>api/user.js</p></blockquote><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">post</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">@/utils/request.js</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">class</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">User</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">/**</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">   * 登录</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">   * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">String</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">username</span><span style="color:#676E95;font-style:italic;"> 用户名</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">   * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">param</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#89DDFF;font-style:italic;">{</span><span style="color:#FFCB6B;font-style:italic;">String</span><span style="color:#89DDFF;font-style:italic;">}</span><span style="color:#676E95;font-style:italic;"> </span><span style="color:#A6ACCD;font-style:italic;">password</span><span style="color:#676E95;font-style:italic;"> 密码</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">   * </span><span style="color:#89DDFF;font-style:italic;">@</span><span style="color:#C792EA;font-style:italic;">returns</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">   */</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#C792EA;">static</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">async</span><span style="color:#A6ACCD;"> </span><span style="color:#F07178;">login</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;font-style:italic;">username</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">password</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#F07178;"> </span><span style="color:#82AAFF;">post</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">/login</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">username</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">password</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>把每个业务模型独立成一个 js 文件，声明一个类通过其属性和方法来实现这个模型相关的数据获取，这样可以大大提升代码的可读性与可维护性。</p><h3 id="后端接口" tabindex="-1">后端接口 <a class="header-anchor" href="#后端接口" aria-label="Permalink to &quot;后端接口&quot;">​</a></h3><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">Data</span></span>
<span class="line"><span style="color:#C792EA;">public</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">class</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">User1</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">private</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">String</span><span style="color:#A6ACCD;"> username</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">private</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">String</span><span style="color:#A6ACCD;"> password</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><div class="language-java"><button title="Copy Code" class="copy"></button><span class="lang">java</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">RestController</span></span>
<span class="line"><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">CrossOrigin</span></span>
<span class="line"><span style="color:#C792EA;">public</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">class</span><span style="color:#A6ACCD;"> </span><span style="color:#FFCB6B;">HelloController</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">@</span><span style="color:#C792EA;">PostMapping</span><span style="color:#89DDFF;">(</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">login</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">)</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">public</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">User1</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">login</span><span style="color:#89DDFF;">(@</span><span style="color:#C792EA;">RequestBody</span><span style="color:#A6ACCD;"> </span><span style="color:#C792EA;">User1</span><span style="color:#A6ACCD;"> </span><span style="color:#A6ACCD;font-style:italic;">user</span><span style="color:#89DDFF;">)</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        System</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">out</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">println</span><span style="color:#89DDFF;">(</span><span style="color:#A6ACCD;">user</span><span style="color:#89DDFF;">);</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;font-style:italic;">return</span><span style="color:#A6ACCD;"> user</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">}</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><h3 id="模拟演示" tabindex="-1">模拟演示 <a class="header-anchor" href="#模拟演示" aria-label="Permalink to &quot;模拟演示&quot;">​</a></h3><p>在需要使用接口的地方，引入对应的业务模型文件，参考如下：</p><div class="language-vue"><button title="Copy Code" class="copy"></button><span class="lang">vue</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">HomeView111</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">h3</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;"> </span><span style="color:#C792EA;">@click</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">login</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;</span><span style="color:#A6ACCD;">post</span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">button</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">div</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">template</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;">import User from &#39;@/api/user.js&#39;;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">export default </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#82AAFF;">data</span><span style="color:#A6ACCD;">() </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    return </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#FFCB6B;">username</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">admin</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#FFCB6B;">password</span><span style="color:#89DDFF;">:</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">12321</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">;</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  methods: </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#C792EA;">async</span><span style="color:#A6ACCD;"> </span><span style="color:#F07178;">login</span><span style="color:#89DDFF;">()</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#C792EA;">const</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">res</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">=</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;font-style:italic;">await</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">User</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">login</span><span style="color:#F07178;">(</span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">username</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">this.</span><span style="color:#A6ACCD;">password</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">      </span><span style="color:#A6ACCD;">console</span><span style="color:#89DDFF;">.</span><span style="color:#82AAFF;">log</span><span style="color:#F07178;">(</span><span style="color:#A6ACCD;">res</span><span style="color:#F07178;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"><span style="color:#F07178;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">;</span></span>
<span class="line"><span style="color:#89DDFF;">&lt;/</span><span style="color:#F07178;">script</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211222104605.png" alt="image-20221122210406447" style="zoom:80%;"><h2 id="集成-css-预处理器-less" tabindex="-1">集成 CSS 预处理器 Less <a class="header-anchor" href="#集成-css-预处理器-less" aria-label="Permalink to &quot;集成 CSS 预处理器 Less&quot;">​</a></h2><p>本项目使用 CSS 预处理器 Less，直接安装为开发依赖即可。</p><p>Vite 内部已帮我们集成了相关的 <code>loader</code>，不需要额外配置。</p><h3 id="安装依赖-4" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-4" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#FFCB6B;">npm</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">i</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">less</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">-D</span></span></code></pre></div><h3 id="如何使用" tabindex="-1">如何使用 <a class="header-anchor" href="#如何使用" aria-label="Permalink to &quot;如何使用&quot;">​</a></h3><p>在 <code>&lt;style&gt;&lt;/style&gt;</code> 样式标签中引用 <code>lang=&quot;less&quot;</code> 即可。</p><div class="language-less"><button title="Copy Code" class="copy"></button><span class="lang">less</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">&lt;</span><span style="color:#FFCB6B;">style</span><span style="color:#A6ACCD;"> lang</span><span style="color:#89DDFF;">=</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">less</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">&gt;&lt;/</span><span style="color:#FFCB6B;">style</span><span style="color:#89DDFF;">&gt;</span></span></code></pre></div><blockquote><p>CSS 命名规范推荐 BEM 命名规范，参考链接：<strong>CSS BEM 书写规范</strong>[21]</p></blockquote><h3 id="全局样式" tabindex="-1">全局样式 <a class="header-anchor" href="#全局样式" aria-label="Permalink to &quot;全局样式&quot;">​</a></h3><p>在 <code>src/style</code> 目录下创建 <code>variables.less</code> 全局样式文件：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">└── src/</span></span>
<span class="line"><span style="color:#A6ACCD;">    ├── style/</span></span>
<span class="line"><span style="color:#A6ACCD;">        ├── variables.less  // 全局样式文件</span></span></code></pre></div><p>在 <code>vite.config.js</code> 配置文件中新增CSS 预处理器相关配置即可实现 less 全局样式：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;font-style:italic;">import</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#F07178;"> </span><span style="color:#A6ACCD;">resolve</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">from</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">path</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;font-style:italic;">export</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;font-style:italic;">default</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">defineConfig</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">css</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">preprocessorOptions</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">less</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#F07178;">modifyVars</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#F07178;">hack</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">`</span><span style="color:#C3E88D;">true; @import (reference) &quot;</span><span style="color:#89DDFF;">${</span><span style="color:#82AAFF;">resolve</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">src/style/variables.less</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">}</span><span style="color:#C3E88D;">&quot;;</span><span style="color:#89DDFF;">`</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#F07178;">math</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">strict</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#F07178;">javascriptEnabled</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><h3 id="样式穿透" tabindex="-1">样式穿透 <a class="header-anchor" href="#样式穿透" aria-label="Permalink to &quot;样式穿透&quot;">​</a></h3><p>在 Vue3 中，改变了以往样式穿透的语法，如果继续使用 <code>::v-deep</code>、<code>/deep/</code>、<code>&gt;&gt;&gt;</code> 等语法的话，会出现一个警告，下面是新的语法：</p><div class="language-less"><button title="Copy Code" class="copy"></button><span class="lang">less</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;">/* 深度选择器 */</span></span>
<span class="line"><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;">deep(selector) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">/* ... */</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/* 插槽选择器 */</span></span>
<span class="line"><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;">slotted(selector) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">/* ... */</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/* 全局选择器 */</span></span>
<span class="line"><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;">global(selector) </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#676E95;font-style:italic;">/* ... */</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><p>至此，一个基于 JavaScript + Vite3 + Vue3 + Vue Router + Pinia + Axios + Less 的前端项目开发环境搭建完毕。</p><p>项目托管在 <strong>GitHub 仓库</strong>[23]，需要的同学可以去下载下来，参考学习。</p><p>接下来增加代码规范约束、提交规范约束、单元测试、自动部署等，让其更完善、更健壮。</p><h1 id="代码规范" tabindex="-1">代码规范 <a class="header-anchor" href="#代码规范" aria-label="Permalink to &quot;代码规范&quot;">​</a></h1><p>随着前端应用逐渐变得大型化和复杂化，在同一个项目中有多个人员参与时，每个人的前端能力程度不等，他们往往会用不同的编码风格和习惯在项目中写代码，长此下去，势必会让项目的健壮性越来越差。解决这些问题，理论上讲，口头约定和代码审查都可以，但是这种方式无法实时反馈，而且沟通成本过高，不够灵活，更关键的是无法把控。不以规矩，不能成方圆，我们不得不在项目使用一些工具来约束代码规范。</p><p>本文讲解如何使用 <strong>EditorConfig + ESLint + Prettier + Stylelint</strong> 组合来实现代码规范化。</p><p>这样做带来好处：</p><ul><li>解决团队之间代码不规范导致的可读性差和可维护性差的问题。</li><li>解决团队成员不同编辑器导致的编码规范不统一问题。</li><li>提前发现代码风格问题，给出对应规范提示，及时修复。</li><li>减少代码审查过程中反反复复的修改过程，节约时间。</li><li>自动格式化，统一编码风格，从此和脏乱差的代码说再见。</li></ul><h2 id="集成-editorconfig-配置" tabindex="-1">集成 EditorConfig 配置 <a class="header-anchor" href="#集成-editorconfig-配置" aria-label="Permalink to &quot;集成 EditorConfig 配置&quot;">​</a></h2><p><strong>EditorConfig</strong> 主要用于统一不同 IDE 编辑器的编码风格。</p><p>在项目根目录下添加 <code>.editorconfig</code> 文件：</p><div class="language-sh"><button title="Copy Code" class="copy"></button><span class="lang">sh</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#676E95;font-style:italic;"># 表示是最顶层的 EditorConfig 配置文件</span></span>
<span class="line"><span style="color:#FFCB6B;">root</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 表示所有文件适用</span></span>
<span class="line"><span style="color:#89DDFF;">[*]</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 缩进风格（tab | space）</span></span>
<span class="line"><span style="color:#FFCB6B;">indent_style</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">space</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 控制换行类型(lf | cr | crlf)</span></span>
<span class="line"><span style="color:#FFCB6B;">end_of_line</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">lf</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 设置文件字符集为 utf-8</span></span>
<span class="line"><span style="color:#FFCB6B;">charset</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">utf-8</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 去除行首的任意空白字符</span></span>
<span class="line"><span style="color:#FFCB6B;">trim_trailing_whitespace</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">true</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 始终在文件末尾插入一个新行</span></span>
<span class="line"><span style="color:#FFCB6B;">insert_final_newline</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 表示仅 md 文件适用以下规则</span></span>
<span class="line"><span style="color:#89DDFF;">[*</span><span style="color:#A6ACCD;">.md</span><span style="color:#89DDFF;">]</span></span>
<span class="line"><span style="color:#FFCB6B;">max_line_length</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">off</span></span>
<span class="line"><span style="color:#FFCB6B;">trim_trailing_whitespace</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">false</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676E95;font-style:italic;"># 表示仅 ts、js、vue、css 文件适用以下规则</span></span>
<span class="line"><span style="color:#89DDFF;">[*</span><span style="color:#A6ACCD;">.{ts,js,vue,css}</span><span style="color:#89DDFF;">]</span></span>
<span class="line"><span style="color:#FFCB6B;">indent_size</span><span style="color:#A6ACCD;"> </span><span style="color:#C3E88D;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span></span></code></pre></div><blockquote><p>很多 IDE 中会默认支持此配置，但是也有些不支持，如：VSCode、Atom、Sublime Text 等。</p><p>具体列表可以参考官网，如果在 VSCode 中使用需要安装 <code>EditorConfig for VS Code</code> 插件。</p></blockquote><p>EditorConfig for VS Code</p><h2 id="集成-eslint-配置" tabindex="-1">集成 ESLint 配置 <a class="header-anchor" href="#集成-eslint-配置" aria-label="Permalink to &quot;集成 ESLint 配置&quot;">​</a></h2><p><strong>ESLint</strong> 是针对 EScript 的一款代码检测工具，它可以检测项目中编写不规范的代码，如果写出不符合规范的代码会被警告。由此我们就可以借助于 ESLint 强大的功能来统一团队的编码规范。</p><h3 id="安装依赖-5" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-5" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><ul><li><strong><code>ESLint</code></strong>[26] - ESLint 本体</li><li><strong><code>eslint-define-config</code></strong>[27] - 改善 ESLint 规范编写体验</li><li><strong><code>eslint-plugin-vue</code></strong>[28] - 适用于 Vue 文件的 ESLint 插件</li><li><strong><code>eslint-config-airbnb-base</code></strong>[29] - Airbnb JavaScript 风格指南</li><li><strong><code>eslint-plugin-import</code></strong>[30] - 使用 <code>eslint-config-airbnb-base</code> 时必须安装的前置插件</li><li><strong><code>vue-eslint-parser</code></strong>[31] - 使用 <code>eslint-plugin-vue</code> 时必须安装的 ESLint 解析器</li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i eslint eslint-define-config eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue vue-eslint-parser -D</span></span></code></pre></div><h3 id="安装插件" tabindex="-1">安装插件 <a class="header-anchor" href="#安装插件" aria-label="Permalink to &quot;安装插件&quot;">​</a></h3><p>Visual Studio Code 编辑器使用 <code>ESLint</code> 配置需要下载插件 <strong>ESLint</strong> 。</p><p>JetBrains 系列编辑器（WebStorm、IntelliJ IDEA 等）则不用额外安装插件。</p><h3 id="创建-eslint-配置文件" tabindex="-1">创建 ESLint 配置文件 <a class="header-anchor" href="#创建-eslint-配置文件" aria-label="Permalink to &quot;创建 ESLint 配置文件&quot;">​</a></h3><p>在项目根目录创建 <code>.eslintrc.js</code> 文件，并填入以下内容：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#C792EA;">const</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span><span style="color:#A6ACCD;"> defineConfig </span><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">require</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">eslint-define-config</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89DDFF;">module.exports</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#82AAFF;">defineConfig</span><span style="color:#A6ACCD;">(</span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">root</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">env</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">browser</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">node</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">jest</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">es6</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">plugins</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">parser</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">vue-eslint-parser</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">parserOptions</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">ecmaVersion</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">latest</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">sourceType</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">module</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">allowImportExportEverywhere</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">ecmaFeatures</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">jsx</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">extends</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">airbnb-base</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">eslint:recommended</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">plugin:vue/vue3-essential</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">plugin:vue/vue3-recommended</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">plugin:prettier/recommended</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">rules</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止使用多余的包</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">import/no-extraneous-dependencies</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 确保在导入路径内一致使用文件扩展名</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">import/extensions</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 确保导入指向可以解析的文件/模块</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">import/no-unresolved</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 首选默认导出导入/首选默认导出</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">import/prefer-default-export</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 要求使用 let 或 const 而不是 var</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-var</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止使用 new 以避免产生副作用</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-new</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">1</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止变量声明与外层作用域的变量同名</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-shadow</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁用 console</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-console</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止标识符中有悬空下划线</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-underscore-dangle</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止在可能与比较操作符相混淆的地方使用箭头函数</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-confusing-arrow</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁用一元操作符 ++ 和 --</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-plusplus</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止对 function 的参数进行重新赋值</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-param-reassign</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁用特定的语法</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-restricted-syntax</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止在变量定义之前使用它们</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-use-before-define</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止直接调用 Object.prototypes 的内置属性</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-prototype-builtins</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止可以在有更简单的可替代的表达式时使用三元操作符</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-unneeded-ternary</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止重复模块导入</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-duplicate-imports</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止在对象中使用不必要的计算属性</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-useless-computed-key</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制使用一致的缩进</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">indent</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制使用骆驼拼写法命名约定</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">camelcase</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制类方法使用 this</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">class-methods-use-this</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 要求构造函数首字母大写</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">new-cap</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制一致地使用 function 声明或表达式</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">func-style</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制一行的最大长度</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">max-len</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 要求 return 语句要么总是指定返回的值，要么不指定</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">consistent-return</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制switch要有default分支</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">default-case</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制剩余和扩展运算符及其表达式之间有空格</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">rest-spread-spacing</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 要求使用 const 声明那些声明后不再被修改的变量</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">prefer-const</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 强制箭头函数的箭头前后使用一致的空格</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">arrow-spacing</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">overrides</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">files</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">*.vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">rules</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 要求组件名称总是多个单词</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">vue/multi-word-component-names</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">}</span><span style="color:#A6ACCD;">)</span><span style="color:#89DDFF;">;</span></span></code></pre></div><blockquote><p>关于更多配置项信息，请前往 ESLint 官网查看 <strong>ESLint-Configuring</strong></p></blockquote><h3 id="创建-eslint-过滤规则" tabindex="-1">创建 ESLint 过滤规则 <a class="header-anchor" href="#创建-eslint-过滤规则" aria-label="Permalink to &quot;创建 ESLint 过滤规则&quot;">​</a></h3><p>在项目根目录添加一个 <code>.eslintignore</code> 文件，内容如下：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">dist</span></span>
<span class="line"><span style="color:#A6ACCD;">node_modules</span></span>
<span class="line"><span style="color:#A6ACCD;">!.prettierrc.js</span></span></code></pre></div><h2 id="集成-prettier-配置" tabindex="-1">集成 Prettier 配置 <a class="header-anchor" href="#集成-prettier-配置" aria-label="Permalink to &quot;集成 Prettier 配置&quot;">​</a></h2><p><strong>Prettier</strong>[33] 是一款强大的代码格式化工具，支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言，基本上前端能用到的文件格式它都可以搞定，是当下最流行的代码格式化工具。</p><h3 id="安装依赖-6" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-6" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i prettier -D</span></span></code></pre></div><h3 id="安装插件-1" tabindex="-1">安装插件 <a class="header-anchor" href="#安装插件-1" aria-label="Permalink to &quot;安装插件&quot;">​</a></h3><p>Visual Studio Code 编辑器使用 <code>Prettier</code> 配置需要下载插件 <strong>Prettier - Code formatter</strong> 。</p><p>Prettier - Code formatter</p><p>JetBrains 系列编辑器（WebStorm、IntelliJ IDEA 等）则不用额外安装插件，可直接使用 <code>Prettier</code> 配置。</p><h3 id="创建-prettier-配置文件" tabindex="-1">创建 Prettier 配置文件 <a class="header-anchor" href="#创建-prettier-配置文件" aria-label="Permalink to &quot;创建 Prettier 配置文件&quot;">​</a></h3><p>Prettier 支持多种格式的<strong>配置文件</strong>[34]，比如 <code>.json</code>、<code>.yml</code>、<code>.yaml</code>、<code>.js</code>等。</p><p>在项目根目录创建 <code>.prettierrc.js</code> 文件，并填入以下内容：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">module.exports</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 一行最多 120 字符</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">printWidth</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">120</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 使用 2 个空格缩进</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">tabWidth</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">2</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 不使用缩进符，而使用空格</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">useTabs</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 行尾需要有分号</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">semi</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 使用单引号</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">singleQuote</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 对象的 key 仅在必要时用引号</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">quoteProps</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">as-needed</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// jsx 不使用单引号，而使用双引号</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">jsxSingleQuote</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 末尾需要有逗号</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">trailingComma</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">all</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 大括号内的首尾需要空格</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">bracketSpacing</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// jsx 标签的反尖括号需要换行</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">jsxBracketSameLine</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 箭头函数，只有一个参数的时候，也需要括号</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">arrowParens</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">always</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 每个文件格式化的范围是文件的全部内容</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">rangeStart</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#F78C6C;">0</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">rangeEnd</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">Infinity,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 不需要写文件开头的 @prettier</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">requirePragma</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 不需要自动在文件开头插入 @prettier</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">insertPragma</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 使用默认的折行标准</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">proseWrap</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">preserve</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 根据显示样式决定 html 要不要折行</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">htmlWhitespaceSensitivity</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">css</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// vue 文件中的 script 和 style 内不用缩进</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">vueIndentScriptAndStyle</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 换行符使用 lf</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">endOfLine</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">lf</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// 格式化嵌入的内容</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">embeddedLanguageFormatting</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">auto</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">  </span><span style="color:#676E95;font-style:italic;">// html, vue, jsx 中每个属性占一行</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">singleAttributePerLine</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">false</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span></code></pre></div><blockquote><p>关于更多配置项信息，请前往 Prettier 官网查看 <strong>Prettier-Options</strong>[35]</p></blockquote><h3 id="创建-prettier-过滤规则" tabindex="-1">创建 Prettier 过滤规则 <a class="header-anchor" href="#创建-prettier-过滤规则" aria-label="Permalink to &quot;创建 Prettier 过滤规则&quot;">​</a></h3><p>在项目根目录添加一个 <code>.prettierignore</code> 文件，内容如下：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">## OS</span></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">DS_Store</span></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">idea</span></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">editorconfig</span></span>
<span class="line"><span style="color:#A6ACCD;">pnpm</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">lock</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">yaml</span></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">npmrc</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;"># Ignored suffix</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">log</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">md</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">svg</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">png</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">ico</span></span>
<span class="line"><span style="color:#89DDFF;">*</span><span style="color:#A6ACCD;">ignore</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">## Local</span></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">husky</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;">## Built</span><span style="color:#89DDFF;">-</span><span style="color:#A6ACCD;">files</span></span>
<span class="line"><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">cache</span></span>
<span class="line"><span style="color:#A6ACCD;">dist</span></span></code></pre></div><h2 id="解决-prettier-和-eslint-冲突" tabindex="-1">解决 Prettier 和 ESLint 冲突 <a class="header-anchor" href="#解决-prettier-和-eslint-冲突" aria-label="Permalink to &quot;解决 Prettier 和 ESLint 冲突&quot;">​</a></h2><p>本项目中的 ESLint 配置使用了 Airbnb JavaScript 风格指南校验，其规则之一是_代码结束后面要加分号_，而在 Prettier 配置文件中加了_代码结束后面不加分号_配置项，从而冲突了。</p><p>解决两者冲突问题，需要用到 <strong>eslint-plugin-prettier</strong> 和 <strong>eslint-config-prettier</strong>。</p><ul><li><code>eslint-plugin-prettier</code> 将 Prettier 的规则设置到 ESLint 的规则中</li><li><code>eslint-config-prettier</code> 关闭 ESLint 中与 Prettier 中会发生冲突的规则</li></ul><p>最后形成优先级：<code>Prettier 配置规则</code> &gt; <code>ESLint 配置规则</code></p><h3 id="安装依赖-7" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-7" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i eslint-plugin-prettier eslint-config-prettier -D</span></span>
<span class="line"><span style="color:#A6ACCD;">复制代码</span></span></code></pre></div><h3 id="修改-eslint-配置文件" tabindex="-1">修改 ESLint 配置文件 <a class="header-anchor" href="#修改-eslint-配置文件" aria-label="Permalink to &quot;修改 ESLint 配置文件&quot;">​</a></h3><p>修改 <code>.eslintrc.js</code> 文件，在 <code>extends</code> 中添加 <code>plugin:prettier/recommended</code> 规则（此规则一定要加在最后）。</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">module.exports = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  extends: [</span></span>
<span class="line"><span style="color:#A6ACCD;">    &#39;airbnb-base&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    &#39;eslint:recommended&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    &#39;plugin:vue/vue3-essential&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    &#39;plugin:vue/vue3-recommended&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    &#39;plugin:prettier/recommended&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">  ],</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><h2 id="自动格式化代码" tabindex="-1">自动格式化代码 <a class="header-anchor" href="#自动格式化代码" aria-label="Permalink to &quot;自动格式化代码&quot;">​</a></h2><p>Visual Studio Code 在 <code>settings.json</code> 设置文件中，增加以下代码：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  &quot;editor.codeActionsOnSave&quot;: {</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;source.fixAll&quot;: true,</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;source.fixAll.eslint&quot;: true</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>WebStorm 打开设置窗口，按如下操作，最后点击 <code>Apply</code> -&gt; <code>OK</code>：</p><h2 id="集成-stylelint-配置" tabindex="-1">集成 Stylelint 配置 <a class="header-anchor" href="#集成-stylelint-配置" aria-label="Permalink to &quot;集成 Stylelint 配置&quot;">​</a></h2><p>Stylelint 是一个强大、先进的 CSS 代码检查器（linter），可以帮助你规避 CSS 代码中的错误并保持一致的编码风格。</p><h3 id="安装依赖-8" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-8" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><ul><li><strong><code>Stylelint</code></strong>[36] - Stylelint 本体</li><li><strong><code>stylelint-config-prettier</code></strong>[37] - 关闭 Stylelint 中与 Prettier 中会发生冲突的规则</li><li><strong><code>stylelint-config-standard</code></strong>[38] - Stylelint 官方推荐规则</li><li><strong><code>stylelint-config-recommended-vue</code></strong>[39] - 检验 vue 文件中的样式</li><li><strong><code>stylelint-order</code></strong>[40] - CSS 属性顺序规则插件</li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i stylelint stylelint-config-prettier stylelint-config-standard stylelint-config-recommended-vue stylelint-order -D</span></span>
<span class="line"><span style="color:#A6ACCD;">复制代码</span></span></code></pre></div><h3 id="安装插件-2" tabindex="-1">安装插件 <a class="header-anchor" href="#安装插件-2" aria-label="Permalink to &quot;安装插件&quot;">​</a></h3><p>Visual Studio Code 编辑器使用 <code>Stylelint</code> 配置需要下载插件 <strong>Stylelint</strong> 。</p><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBBnibibLViaaLE5U9HoxrpIRE4AZnLsKoRKibPQuhPQvLtLNgB4DrbG0sGA/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">Stylelint</p><p>JetBrains 系列编辑器（WebStorm、IntelliJ IDEA 等）则不用额外安装插件。</p><h3 id="创建-stylelint-配置文件" tabindex="-1">创建 Stylelint 配置文件 <a class="header-anchor" href="#创建-stylelint-配置文件" aria-label="Permalink to &quot;创建 Stylelint 配置文件&quot;">​</a></h3><p>在项目根目录创建 <code>.stylelintrc.js</code> 文件，并填入以下内容：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">module.exports</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">root</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">defaultSeverity</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">error</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">extends</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">stylelint-config-standard</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">stylelint-config-prettier</span><span style="color:#89DDFF;">&#39;</span></span>
<span class="line"><span style="color:#A6ACCD;">  ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">plugins</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">stylelint-order</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">rules</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 不允许未知函数</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">function-no-unknown</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 指定类选择器的模式</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">selector-class-pattern</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止空源码</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">no-empty-source</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 指定字符串使用单引号</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">string-quotes</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">single</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止未知的@规则</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">at-rule-no-unknown</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">ignoreAtRules</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">tailwind</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">apply</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">variants</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">responsive</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">screen</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">function</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">if</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">each</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">include</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">mixin</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">        ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 指定@规则名的大小写</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">at-rule-name-case</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">lower</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 指定缩进</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#F07178;">indentation</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F78C6C;">2</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">severity</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">warning</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止未知的伪类选择器</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">selector-pseudo-class-no-unknown</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">ignorePseudoClasses</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">global</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">    </span><span style="color:#676E95;font-style:italic;">// 禁止未知的伪元素选择器</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">selector-pseudo-element-no-unknown</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#F07178;">ignorePseudoElements</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">v-deep</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">order/properties-order</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">position</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">top</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">right</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">bottom</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">left</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">z-index</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">display</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">justify-content</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">align-items</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">float</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">clear</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">overflow</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">overflow-x</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">overflow-y</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">margin</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">margin-top</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">margin-right</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">margin-bottom</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">margin-left</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">padding</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">padding-top</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">padding-right</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">padding-bottom</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">padding-left</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">min-width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">max-width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">height</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">min-height</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">max-height</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">font-size</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">font-family</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">font-weight</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-style</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-color</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-top</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-top-style</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-top-width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-top-color</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-right</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-right-style</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-right-width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-right-color</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-bottom</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-bottom-style</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-bottom-width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-bottom-color</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-left</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-left-style</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-left-width</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-left-color</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">border-radius</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">text-align</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">text-justify</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">text-indent</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">text-overflow</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">text-decoration</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">white-space</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">color</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">background</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">background-position</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">background-repeat</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">background-size</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">background-color</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">background-clip</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">opacity</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">filter</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">list-style</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">outline</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">visibility</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">box-shadow</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">text-shadow</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">resize</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">transition</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><h3 id="创建-stylelint-过滤规则" tabindex="-1">创建 Stylelint 过滤规则 <a class="header-anchor" href="#创建-stylelint-过滤规则" aria-label="Permalink to &quot;创建 Stylelint 过滤规则&quot;">​</a></h3><p>在项目根目录添加一个 <code>.stylelintignore</code> 文件，内容如下：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;"># </span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">stylelintignore</span></span>
<span class="line"><span style="color:#A6ACCD;"># 旧的不需打包的样式库</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">min</span><span style="color:#89DDFF;">.</span><span style="color:#A6ACCD;">css</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;"># 其他类型文件</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">js</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">jpg</span></span>
<span class="line"><span style="color:#89DDFF;">*.</span><span style="color:#A6ACCD;">woff</span></span>
<span class="line"></span>
<span class="line"><span style="color:#A6ACCD;"># 测试和打包目录</span></span>
<span class="line"><span style="color:#89DDFF;">/</span><span style="color:#C3E88D;">test</span><span style="color:#89DDFF;">/</span></span>
<span class="line"><span style="color:#89DDFF;">/</span><span style="color:#A6ACCD;">dist</span><span style="color:#676E95;font-style:italic;">/*</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/public/*</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">public/*</span></span>
<span class="line"><span style="color:#676E95;font-style:italic;">/node_modules/</span></span></code></pre></div><h3 id="启用-vue-文件支持" tabindex="-1">启用 Vue 文件支持 <a class="header-anchor" href="#启用-vue-文件支持" aria-label="Permalink to &quot;启用 Vue 文件支持&quot;">​</a></h3><p><code>Stylelint</code> v14 版本默认不支持 vue 文件中的 style 代码自动检测，详情查看<strong>官方迁移指南</strong>[41]</p><h4 id="安装依赖-9" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-9" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h4><ul><li><strong><code>stylelint-config-html</code></strong>[42] - 解析 vue 文件</li><li><strong><code>postcss-html</code></strong>[43] - 使用 <code>stylelint-config-html</code> 依赖的模块</li><li><strong><code>postcss-less</code></strong>[44] - 对 less 文件进行解析</li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i stylelint-config-html postcss-html postcss-less -D</span></span></code></pre></div><h4 id="修改-stylelint-配置文件" tabindex="-1">修改 Stylelint 配置文件 <a class="header-anchor" href="#修改-stylelint-配置文件" aria-label="Permalink to &quot;修改 Stylelint 配置文件&quot;">​</a></h4><p>修改 <code>.stylelintrc.js</code> 文件，添加如下配置：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">module.exports</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">=</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  </span><span style="color:#F07178;">overrides</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">files</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">*.vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">**/*.vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">*.html</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">**/*.html</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">extends</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">stylelint-config-html</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">rules</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 指定关键帧名称的模式</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">keyframes-name-pattern</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">null,</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 禁止未知的伪类选择器</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">selector-pseudo-class-no-unknown</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#F07178;">ignorePseudoClasses</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">deep</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">global</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">        ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">        </span><span style="color:#676E95;font-style:italic;">// 禁止未知的伪元素选择器</span></span>
<span class="line"><span style="color:#A6ACCD;">        </span><span style="color:#89DDFF;">&#39;</span><span style="color:#F07178;">selector-pseudo-element-no-unknown</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#FF9CAC;">true</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">            </span><span style="color:#F07178;">ignorePseudoElements</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">v-deep</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">v-global</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">v-slotted</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">          </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">        ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">files</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">*.less</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">**/*.less</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">customSyntax</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">postcss-less</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">      </span><span style="color:#F07178;">extends</span><span style="color:#89DDFF;">:</span><span style="color:#A6ACCD;"> [</span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">stylelint-config-standard</span><span style="color:#89DDFF;">&#39;</span><span style="color:#89DDFF;">,</span><span style="color:#A6ACCD;"> </span><span style="color:#89DDFF;">&#39;</span><span style="color:#C3E88D;">stylelint-config-recommended-vue</span><span style="color:#89DDFF;">&#39;</span><span style="color:#A6ACCD;">]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#A6ACCD;">    </span><span style="color:#89DDFF;">},</span></span>
<span class="line"><span style="color:#A6ACCD;">  ]</span><span style="color:#89DDFF;">,</span></span>
<span class="line"><span style="color:#89DDFF;">};</span></span></code></pre></div><h4 id="修改-visual-studio-code-工作区配置" tabindex="-1">修改 Visual Studio Code 工作区配置 <a class="header-anchor" href="#修改-visual-studio-code-工作区配置" aria-label="Permalink to &quot;修改 Visual Studio Code 工作区配置&quot;">​</a></h4><p>Visual Studio Code 在 <code>settings.json</code> 设置文件中，增加以下代码：</p><div class="language-js"><button title="Copy Code" class="copy"></button><span class="lang">js</span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#89DDFF;">{</span></span>
<span class="line"><span style="color:#F07178;">  </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">stylelint.validate</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">: [</span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">css</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">less</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">postcss</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">scss</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">vue</span><span style="color:#89DDFF;">&quot;</span><span style="color:#89DDFF;">,</span><span style="color:#F07178;"> </span><span style="color:#89DDFF;">&quot;</span><span style="color:#C3E88D;">sass</span><span style="color:#89DDFF;">&quot;</span><span style="color:#F07178;">]</span></span>
<span class="line"><span style="color:#89DDFF;">}</span></span></code></pre></div><h2 id="集成-husky-和-lint-staged" tabindex="-1">集成 husky 和 lint-staged <a class="header-anchor" href="#集成-husky-和-lint-staged" aria-label="Permalink to &quot;集成 husky 和 lint-staged&quot;">​</a></h2><p>在项目中已集成 ESLint 和 Prettier，在编码时，这些工具可以对代码进行实时校验，在一定程度上能有效规范所写代码，但有些人可能觉得这些限制很麻烦，从而选择视“提示”而不见，依旧按自己编程风格来写代码，或者干脆禁用掉这些工具，开发完成就直接把代码提交到了仓库，日积月累，ESLint 也就形同虚设。</p><p>所以，还需要做一些限制，让没通过 ESLint 检测和修复的代码禁止提交，从而保证仓库代码都是符合规范的。</p><p>为了解决这个问题，需要用到 Git Hook，在本地执行 <code>git commit</code> 的时候，就对所提交的代码进行 ESLint 检测和修复（即执行 <code>eslint \--fix</code>），如果这些代码没通过 ESLint 规则校验，则禁止提交。</p><p>实现这一功能，需要借助 <strong>husky</strong>[45] + <strong>lint-staged</strong>[46] 。</p><h3 id="配置-husky" tabindex="-1">配置 husky <a class="header-anchor" href="#配置-husky" aria-label="Permalink to &quot;配置 husky&quot;">​</a></h3><blockquote><p>注意：本项目使用 husky 6.x 版本，6.x 版本配置方式跟之前版本有较大差异，当发现配置方法不一致时，一切以 <strong>husky 官网</strong>[47]为准。</p></blockquote><p>使用 <code>husky-init</code> 命令快速在项目初始化 <code>husky</code> 配置：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;"># 初始化仓库</span></span>
<span class="line"><span style="color:#A6ACCD;">git init</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;"># 初始化</span></span>
<span class="line"><span style="color:#A6ACCD;">npx husky-init</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;"># 安装依赖</span></span>
<span class="line"><span style="color:#A6ACCD;">npm install</span></span></code></pre></div><p>husky 包含很多 <code>hook</code>（钩子），常用有：<code>pre-commit</code>、<code>commit-msg</code>。</p><p>使用 <code>pre-commit</code> 来触发 ESLint 命令，修改 <code>.husky/pre-commit</code> 文件触发命令：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">eslint --fix ./src --ext .vue,.js,.ts</span></span></code></pre></div><p><code>pre-commit</code> hook 文件作用是：当执行 <code>git commit \-m &quot;xxx&quot;</code> 时，会先对 <code>src</code> 目录下所有的 <code>.vue</code>、<code>.js</code>、<code>.ts</code> 文件执行 <code>eslint \--fix</code> 命令，如果 ESLint 通过，成功 <code>commit</code>，否则终止 <code>commit</code>。</p><p>但是又存在一个问题：有时候明明只改动了一两个文件，却要对所有的文件执行 <code>eslint \--fix</code>。</p><p>假如这是一个历史项目，在中途配置了 ESLint 规则，那么在提交代码时，也会对其他未修改的“历史”文件都进行检查，可能会造成大量文件出现 ESLint 错误，显然这不是我们想要的结果。</p><p>所以只需要用 ESLint 修复此次写的代码，而不去影响其他的代码，此时需要借助 <strong>lint-staged</strong> 工具。</p><h3 id="配置-lint-staged" tabindex="-1">配置 lint-staged <a class="header-anchor" href="#配置-lint-staged" aria-label="Permalink to &quot;配置 lint-staged&quot;">​</a></h3><p>lint-staged 一般结合 husky 来使用，它可以让 husky 的 <code>hook</code> 触发的命令只作用于 <code>git</code> 暂存区的文件，而不会影响到其他文件。</p><h4 id="安装依赖-10" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-10" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h4><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i lint-staged -D</span></span></code></pre></div><h4 id="新增配置" tabindex="-1">新增配置 <a class="header-anchor" href="#新增配置" aria-label="Permalink to &quot;新增配置&quot;">​</a></h4><p>在 <code>package.json</code> 里增加 <code>lint-staged</code> 配置项：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  &quot;lint-staged&quot;: {</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;*.{js,jsx,ts,tsx}&quot;: [</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;prettier --write&quot;,</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;eslint --fix&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">    ],</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;*.vue&quot;: [</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;prettier --write&quot;,</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;eslint --fix&quot;,</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;stylelint --fix&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">    ],</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;*.{html,vue,vss,sass,less}&quot;: [</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;prettier --write&quot;,</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;stylelint --fix&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">    ],</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;package.json&quot;: [</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;prettier --write&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">    ],</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;*.md&quot;: [</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;prettier --write&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">    ]</span></span>
<span class="line"><span style="color:#A6ACCD;">  },</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><h4 id="修改触发命令" tabindex="-1">修改触发命令 <a class="header-anchor" href="#修改触发命令" aria-label="Permalink to &quot;修改触发命令&quot;">​</a></h4><p>修改 <code>.husky/pre-commit</code> 文件触发命令为：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npx lint-staged</span></span></code></pre></div><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBh2WNXj2HhhbkibgXmYZBgSqHvic2CZVDKV5TXFEkA8wTDITvTycEv42g/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">pre-commit</p><p>经过以上配置之后，就可以在每次提交之前对所有代码进行格式化，保证线上代码的规范性。</p><h1 id="提交规范" tabindex="-1">提交规范 <a class="header-anchor" href="#提交规范" aria-label="Permalink to &quot;提交规范&quot;">​</a></h1><p>多人协作项目中，在提交代码环节，也存在一种情况：不能保证每个人对提交信息的准确描述，因此会出现提交信息紊乱、风格不一致的情况。如果 <code>git commit</code> 的描述信息精准，在后期维护和 Bug 处理时会变得有据可查，项目开发周期内还可以根据规范的提交信息快速生成开发日志，从而方便我们追踪项目和把控进度。</p><p>社区最流行、最知名、最受认可的 <strong>Angular</strong> 团队提交规范：</p><img src="https://edu-8673.oss-cn-beijing.aliyuncs.com/img2022.12.30/202211222111877.png" alt="image-20221122211133669" style="zoom:80%;"><h2 id="commit-message-格式规范" tabindex="-1">Commit Message 格式规范 <a class="header-anchor" href="#commit-message-格式规范" aria-label="Permalink to &quot;Commit Message 格式规范&quot;">​</a></h2><p><code>commit message</code> 由 Header、Body、Footer 组成。</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;Header&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;Body&gt;</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">&lt;Footer&gt;</span></span></code></pre></div><h3 id="header" tabindex="-1">Header <a class="header-anchor" href="#header" aria-label="Permalink to &quot;Header&quot;">​</a></h3><p>Header 部分包括三个字段 type（必需）、scope（可选）和 subject（必需）。</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&lt;type&gt;(&lt;scope&gt;): &lt;subject&gt;</span></span></code></pre></div><p><strong>type</strong></p><p>type 用于说明 commit 的提交类型（必须是以下几种之一）。</p><table><thead><tr><th style="text-align:left;">值</th><th style="text-align:left;">描述</th></tr></thead><tbody><tr><td style="text-align:left;">feat</td><td style="text-align:left;">新增功能</td></tr><tr><td style="text-align:left;">fix</td><td style="text-align:left;">修复问题</td></tr><tr><td style="text-align:left;">docs</td><td style="text-align:left;">文档变更</td></tr><tr><td style="text-align:left;">style</td><td style="text-align:left;">代码格式（不影响功能，例如空格、分号等格式修正）</td></tr><tr><td style="text-align:left;">refactor</td><td style="text-align:left;">代码重构</td></tr><tr><td style="text-align:left;">perf</td><td style="text-align:left;">改善性能</td></tr><tr><td style="text-align:left;">test</td><td style="text-align:left;">测试</td></tr><tr><td style="text-align:left;">build</td><td style="text-align:left;">变更项目构建或外部依赖（例如 scopes: webpack、gulp、npm 等）</td></tr><tr><td style="text-align:left;">ci</td><td style="text-align:left;">更改持续集成软件的配置文件和 package 中的 scripts 命令，例如 scopes: Travis, Circle 等</td></tr><tr><td style="text-align:left;">chore</td><td style="text-align:left;">变更构建流程或辅助工具</td></tr><tr><td style="text-align:left;">revert</td><td style="text-align:left;">代码回退</td></tr></tbody></table><p><strong>scope</strong></p><p>scope 用于指定本次 commit 影响的范围。</p><p>scope 依据项目而定，例如在业务项目中可以依据菜单或者功能模块划分，如果是组件库开发，则可以依据组件划分。</p><p><strong>subject</strong></p><p>subject 是本次 commit 的简洁描述，长度约定在 50 个字符以内，通常遵循以下几个规范：</p><ul><li>用动词开头，第一人称现在时表述，例如：change 代替 changed 或 changes</li><li>第一个字母小写</li><li>结尾不加句号（.）</li></ul><h3 id="body" tabindex="-1">Body <a class="header-anchor" href="#body" aria-label="Permalink to &quot;Body&quot;">​</a></h3><p>body 是对本次 commit 的详细描述，可以分成多行。</p><p>跟 subject 类似，用动词开头，body 应该说明修改的原因和更改前后的行为对比。</p><h3 id="footer" tabindex="-1">Footer <a class="header-anchor" href="#footer" aria-label="Permalink to &quot;Footer&quot;">​</a></h3><p>如果本次提交的代码是突破性的变更或关闭缺陷，则 Footer 必需，否则可以省略。</p><ul><li><p>突破性的变更</p><p>当前代码与上一个版本有突破性改变，则 Footer 以 BREAKING CHANGE 开头，后面是对变动的描述、以及变动的理由。</p></li><li><p>关闭缺陷</p><p>如果当前提交是针对特定的 issue，那么可以在 Footer 部分填写需要关闭的单个 issue 或一系列 issues。</p></li></ul><h3 id="参考例子" tabindex="-1">参考例子 <a class="header-anchor" href="#参考例子" aria-label="Permalink to &quot;参考例子&quot;">​</a></h3><p>feat</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">feat(browser): onUrlChange event (popstate/hashchange/polling)</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">Added new event to browser:</span></span>
<span class="line"><span style="color:#A6ACCD;">- forward popstate event if available</span></span>
<span class="line"><span style="color:#A6ACCD;">- forward hashchange event if popstate not available</span></span>
<span class="line"><span style="color:#A6ACCD;">- do polling when neither popstate nor hashchange available</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">Breaks $browser.onHashChange, which was removed (use onUrlChange instead)</span></span></code></pre></div><p>fix</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">fix(compile): couple of unit tests for IE9</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">Older IEs serialize html uppercased, but IE9 does not...</span></span>
<span class="line"><span style="color:#A6ACCD;">Would be better to expect case insensitive, unfortunately jasmine does</span></span>
<span class="line"><span style="color:#A6ACCD;">not allow to user regexps for throw expectations.</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">Closes #392</span></span>
<span class="line"><span style="color:#A6ACCD;">Breaks foo.bar api, foo.baz should be used instead</span></span></code></pre></div><p>style</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">style(location): add couple of missing semi colons</span></span></code></pre></div><p>chore</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">chore(release): v3.4.2</span></span></code></pre></div><h2 id="集成-cz-git-实现规范提交" tabindex="-1">集成 cz-git 实现规范提交 <a class="header-anchor" href="#集成-cz-git-实现规范提交" aria-label="Permalink to &quot;集成 cz-git 实现规范提交&quot;">​</a></h2><blockquote><p>一款工程性更强，轻量级，高度自定义，标准输出格式的 <strong>commitizen</strong>[50] 适配器</p><p>官方网站：<strong>cz-git</strong>[51]</p></blockquote><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBjaaTBbMHy0zXQSAibw601ovBrgkfNvn3IzcrPjiaicPeibDbBJ2kLw0pcQ/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">cz-git</p><h3 id="安装依赖-11" tabindex="-1">安装依赖 <a class="header-anchor" href="#安装依赖-11" aria-label="Permalink to &quot;安装依赖&quot;">​</a></h3><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm install -D cz-git</span></span></code></pre></div><h3 id="指定适配器" tabindex="-1">指定适配器 <a class="header-anchor" href="#指定适配器" aria-label="Permalink to &quot;指定适配器&quot;">​</a></h3><p>修改 <code>package.json</code> 文件，添加 <code>config</code> 指定使用的适配器</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  &quot;scripts&quot;: {},</span></span>
<span class="line"><span style="color:#A6ACCD;">  &quot;config&quot;: {</span></span>
<span class="line"><span style="color:#A6ACCD;">    &quot;commitizen&quot;: {</span></span>
<span class="line"><span style="color:#A6ACCD;">      &quot;path&quot;: &quot;node_modules/cz-git&quot;</span></span>
<span class="line"><span style="color:#A6ACCD;">    }</span></span>
<span class="line"><span style="color:#A6ACCD;">  }</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><h3 id="自定义配置-可选" tabindex="-1">自定义配置（可选） <a class="header-anchor" href="#自定义配置-可选" aria-label="Permalink to &quot;自定义配置（可选）&quot;">​</a></h3><p><strong>cz-git 与 commitlint[52] 进行联动给予校验信息</strong>，所以可以编写于 <strong>commitlint</strong>[53] 配置文件之中。</p><p>例如：(<strong>⇒ 配置模板</strong>[54])</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">/** @type {import(&#39;cz-git&#39;).UserConfig} */</span></span>
<span class="line"><span style="color:#A6ACCD;">module.exports = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  extends: [&#39;@commitlint/config-conventional&#39;],</span></span>
<span class="line"><span style="color:#A6ACCD;">  rules: {},</span></span>
<span class="line"><span style="color:#A6ACCD;">  prompt: {</span></span>
<span class="line"><span style="color:#A6ACCD;">    useEmoji: false,</span></span>
<span class="line"><span style="color:#A6ACCD;">    emojiAlign: &#39;center&#39;,</span></span>
<span class="line"><span style="color:#A6ACCD;">    allowCustomIssuePrefixs: false,</span></span>
<span class="line"><span style="color:#A6ACCD;">    allowEmptyIssuePrefixs: false,</span></span>
<span class="line"><span style="color:#A6ACCD;">  },</span></span>
<span class="line"><span style="color:#A6ACCD;">};</span></span></code></pre></div><p>本项目配置文件可参考：<strong>commitlint.config.js</strong></p><h3 id="全局使用" tabindex="-1">全局使用 <a class="header-anchor" href="#全局使用" aria-label="Permalink to &quot;全局使用&quot;">​</a></h3><blockquote><p>全局安装的好处在于：在任何项目下都可以利用 <code>cz</code> 或 <code>git cz</code> 命令启动命令行工具，生成标准化 commit message</p></blockquote><h4 id="安装全局依赖" tabindex="-1">安装全局依赖 <a class="header-anchor" href="#安装全局依赖" aria-label="Permalink to &quot;安装全局依赖&quot;">​</a></h4><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm install -g cz-git commitizen</span></span></code></pre></div><h4 id="全局配置适配器类型" tabindex="-1">全局配置适配器类型 <a class="header-anchor" href="#全局配置适配器类型" aria-label="Permalink to &quot;全局配置适配器类型&quot;">​</a></h4><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">echo &#39;{ &quot;path&quot;: &quot;cz-git&quot; }&#39; &gt; ~/.czrc</span></span></code></pre></div><h4 id="自定义配置-可选-1" tabindex="-1">自定义配置（可选） <a class="header-anchor" href="#自定义配置-可选-1" aria-label="Permalink to &quot;自定义配置（可选）&quot;">​</a></h4><p><strong>方式一：</strong> 编辑 <code>~/.czrc</code> 文件以 <strong>json</strong> 形式添加配置，例如：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">{</span></span>
<span class="line"><span style="color:#A6ACCD;">  &quot;path&quot;: &quot;cz-git&quot;,</span></span>
<span class="line"><span style="color:#A6ACCD;">  &quot;useEmoji&quot;: true</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p><strong>方式二：与 commitlint[56] 配合</strong>，在 <code>$HOME</code> 路径下创建配置文件 (<strong>↓ 配置模板</strong>[57])</p><h2 id="集成-commitlint-验证规范提交" tabindex="-1">集成 commitlint 验证规范提交 <a class="header-anchor" href="#集成-commitlint-验证规范提交" aria-label="Permalink to &quot;集成 commitlint 验证规范提交&quot;">​</a></h2><p>在“代码规范”章节中提到，尽管制定了规范，但在多人协作的项目中，总有些人依旧我行我素。</p><p>因此提交代码这个环节，也增加一个限制：<strong>只让符合 Angular 规范的 commit message 通过</strong>。</p><p>此功能需借助 <code>@commitlint/config-conventional</code> 和 <code>@commitlint/cli</code> 工具来实现。</p><h3 id="安装" tabindex="-1">安装 <a class="header-anchor" href="#安装" aria-label="Permalink to &quot;安装&quot;">​</a></h3><ul><li><strong><code>@commitlint/cli</code></strong>[58] - Commitlint 本体</li><li><strong><code>@commitlint/config-conventional</code></strong>[59] - 通用提交规范</li></ul><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npm i @commitlint/cli @commitlint/config-conventional -D</span></span></code></pre></div><h3 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">​</a></h3><p>在项目根目录创建 <code>commitlint.config.js</code> 文件，并填入以下内容：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">module.exports = {</span></span>
<span class="line"><span style="color:#A6ACCD;">  extends: [&#39;@commitlint/config-conventional&#39;]</span></span>
<span class="line"><span style="color:#A6ACCD;">}</span></span></code></pre></div><p>使用 husky 命令在 <code>.husky</code> 目录下创建 <code>commit-msg</code> 文件，并在此执行验证命令：</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">npx husky add .husky/commit-msg &quot;npx --no-install commitlint --edit $1&quot;</span></span></code></pre></div><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBoRnnPEKE6fkib6lPzPspF11rwFNMt1ZQo5YJ7T7SzpC9J35dIwonnqg/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">commit-msg</p><blockquote><p>本项目完整代码托管在 <strong>GitHub 仓库</strong>[60]，欢迎点亮小星星 🌟🌟</p></blockquote><h1 id="自动部署" tabindex="-1">自动部署 <a class="header-anchor" href="#自动部署" aria-label="Permalink to &quot;自动部署&quot;">​</a></h1><p>本章节将介绍如何使用 CI（Continuous Integration 持续集成）服务来完成项目部署工作。</p><p>常见的 CI 工具有 GitHub Actions、GitLab CI、Travis CI、Circle CI 等。</p><p>本项目使用 <code>GitHub Actions</code> 来完成这一操作。</p><p>🔗 参考链接：<strong>GitHub Actions 入门教程</strong>[61]</p><h2 id="创建-github-仓库" tabindex="-1">创建 GitHub 仓库 <a class="header-anchor" href="#创建-github-仓库" aria-label="Permalink to &quot;创建 GitHub 仓库&quot;">​</a></h2><p>因为 GitHub Actions 只对 GitHub 仓库有效，所以<strong>创建 GitHub 仓库</strong>[62]来托管项目代码。</p><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBFcsrwNsevDEDefwNo2icicnoXYhdp6OLwMibQF3LibpkYwiar4t9z2zQopg/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">创建 GitHub 仓库</p><ul><li><code>master</code> 分支存储项目源代码</li><li><code>gh-pages</code> 分支存储打包后的静态文件</li></ul><h2 id="创建-github-token" tabindex="-1">创建 GitHub Token <a class="header-anchor" href="#创建-github-token" aria-label="Permalink to &quot;创建 GitHub Token&quot;">​</a></h2><p>创建一个有 <strong>repo</strong> 和 <strong>workflow</strong> 权限的 <strong>GitHub Token</strong>[63]</p><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBNP6vGUMUSXzUgE7ky2EdR8icvKUhNtjbZIZ26NjhglEZKO3bkDBKl5Q/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">创建 GitHub Token</p><blockquote><p>注意：新生成的 Token 只会显示一次。</p></blockquote><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBQuOE3l0TW8LDib8x311JBJKX3mThmNQZLaANWl482hiaODNMuQhhEWWA/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">Token</p><h2 id="添加-actions-secret" tabindex="-1">添加 Actions secret <a class="header-anchor" href="#添加-actions-secret" aria-label="Permalink to &quot;添加 Actions secret&quot;">​</a></h2><p>将上述创建的 Token 添加到 GitHub 仓库中的 <code>Secrets</code> 里，并将这个新增的 <code>secret</code> 命名为 <code>VITE_VUE_DEPLOY</code> 。</p><p>步骤：仓库 -&gt; <code>Settings</code> -&gt; <code>Secrets</code> -&gt; <code>Actions</code> -&gt; <code>New repository secret</code>。</p><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNB11cXYnVM4syD6bg7skjaNL8lLKnILz3rU99VlVvRNnUSAibic2ibyejcg/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">New secret</p><blockquote><p>注意：新创建的 secret <code>VITE_VUE_DEPLOY</code> 在 Actions 配置文件中要用到，两个地方需保持一致！</p></blockquote><h2 id="修改-package-json" tabindex="-1">修改 package.json <a class="header-anchor" href="#修改-package-json" aria-label="Permalink to &quot;修改 package.json&quot;">​</a></h2><p>打开 <code>package.json</code> 文件，新增 <code>homepage</code> 字段，表示该应用发布后的根目录（参见<strong>官方文档</strong>[64]）。</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">&quot;homepage&quot;: &quot;https://[username].github.io/github-actions-demo&quot;,</span></span></code></pre></div><p>上面代码中，将 <code>[username]</code> 替换成你的 GitHub 用户名，参见<strong>范例</strong>[65]。</p><h2 id="创建-actions-配置文件" tabindex="-1">创建 Actions 配置文件 <a class="header-anchor" href="#创建-actions-配置文件" aria-label="Permalink to &quot;创建 Actions 配置文件&quot;">​</a></h2><p>（1）在项目根目录下创建 <code>.github</code> 目录。</p><p>（2）在 <code>.github</code> 目录下创建 <code>workflows</code> 目录。</p><p>（3）在 <code>workflows</code> 目录下创建 <code>deploy.yml</code> 文件。</p><p><img src="https://mmbiz.qpic.cn/mmbiz/pfCCZhlbMQTKJwX2UrGjOSEIicyga1nNBkmwLaNrdtdPzu97SkWI8vnKWiaiclAkia9ibC6OsUyWWibT1wGqbP2BCcUw/640?wx_fmt=jpeg&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片">.github/workflows/deploy.yml</p><div class="language-"><button title="Copy Code" class="copy"></button><span class="lang"></span><pre class="shiki material-theme-palenight"><code><span class="line"><span style="color:#A6ACCD;">name: Vite Vue Deploy</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">on:</span></span>
<span class="line"><span style="color:#A6ACCD;">  push:</span></span>
<span class="line"><span style="color:#A6ACCD;">    # master 分支有 push 时触发</span></span>
<span class="line"><span style="color:#A6ACCD;">    branches: [master]</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">jobs:</span></span>
<span class="line"><span style="color:#A6ACCD;">  deploy:</span></span>
<span class="line"><span style="color:#A6ACCD;">    # 指定虚拟机环境</span></span>
<span class="line"><span style="color:#A6ACCD;">    runs-on: ubuntu-latest</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">    strategy:</span></span>
<span class="line"><span style="color:#A6ACCD;">      matrix:</span></span>
<span class="line"><span style="color:#A6ACCD;">        node-version: [14.x, 16.x]</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">    steps:</span></span>
<span class="line"><span style="color:#A6ACCD;">      - name: Checkout</span></span>
<span class="line"><span style="color:#A6ACCD;">        # 拉取 GitHub 仓库代码</span></span>
<span class="line"><span style="color:#A6ACCD;">        uses: actions/checkout@v3</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">      - name: Use Node.js ${{ matrix.node-version }}</span></span>
<span class="line"><span style="color:#A6ACCD;">        # 设定 Node.js 环境</span></span>
<span class="line"><span style="color:#A6ACCD;">        uses: actions/setup-node@v3</span></span>
<span class="line"><span style="color:#A6ACCD;">        with:</span></span>
<span class="line"><span style="color:#A6ACCD;">          node-version: ${{ matrix.node-version }}</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">      - name: Install</span></span>
<span class="line"><span style="color:#A6ACCD;">        # 安装依赖</span></span>
<span class="line"><span style="color:#A6ACCD;">        run: npm install</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">      - name: Build</span></span>
<span class="line"><span style="color:#A6ACCD;">        # 打包</span></span>
<span class="line"><span style="color:#A6ACCD;">        run: npm run build</span></span>
<span class="line"><span style="color:#A6ACCD;"></span></span>
<span class="line"><span style="color:#A6ACCD;">      - name: Deploy</span></span>
<span class="line"><span style="color:#A6ACCD;">        uses: JamesIves/github-pages-deploy-action@v4</span></span>
<span class="line"><span style="color:#A6ACCD;">        with:</span></span>
<span class="line"><span style="color:#A6ACCD;">          # 部署打包目录</span></span>
<span class="line"><span style="color:#A6ACCD;">          folder: dist</span></span>
<span class="line"><span style="color:#A6ACCD;">          # 密钥名</span></span>
<span class="line"><span style="color:#A6ACCD;">          token: ${{ secrets.VITE_VUE_DEPLOY }}</span></span>
<span class="line"><span style="color:#A6ACCD;">          # 分支</span></span>
<span class="line"><span style="color:#A6ACCD;">          branch: gh-pages</span></span></code></pre></div><blockquote><p>🔗 通过此链接 <strong>ElanYoung.github.io/vite-vue-js…</strong>[66] 即可访问本项目</p></blockquote><h1 id="文章总结" tabindex="-1">文章总结 <a class="header-anchor" href="#文章总结" aria-label="Permalink to &quot;文章总结&quot;">​</a></h1><p>本文从技术选项到架构搭建、从代码规范约束到提交信息规范约束，一步一步带领大家如何从一个最简单的前端项目骨架到规范的前端工程化环境，基本涵盖前端项目开发的整个流程，特别适合刚接触前端工程化的同学学习。</p><p>因篇幅较长，所涉及技术点较多，难免会出现错误，希望大家多多指正，谢谢大家！</p></div></div></main><footer class="VPDocFooter" data-v-6b87e69f data-v-37656e44><!--[--><!--]--><!----><nav class="prev-next" data-v-37656e44><div class="pager" data-v-37656e44><a class="pager-link prev" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue3/Vue3%E9%AB%98%E7%BA%A7.html" data-v-37656e44><span class="desc" data-v-37656e44>Previous page</span><span class="title" data-v-37656e44>Vue3高级</span></a></div><div class="pager" data-v-37656e44><a class="pager-link next" href="/notebook/1%E3%80%81%E5%AD%A6%E5%89%8D%E7%AB%AF/3%E3%80%81Vue/Vue2/Vue2%E9%A1%B9%E7%9B%AE.html" data-v-37656e44><span class="desc" data-v-37656e44>Next page</span><span class="title" data-v-37656e44>项目实战</span></a></div></nav></footer><!--[--><!--]--></div></div></div><!--[--><!--]--></div></div><!----><!--[--><!--]--></div></div>
    <script>window.__VP_HASH_MAP__=JSON.parse("{\"2、数据库_mysql_mysql面试_基础.md\":\"40da680a\",\"1、学前端_5、小程序_小程序项目.md\":\"60a1629b\",\"1、学前端_4、node_知识篇.md\":\"a7fb500e\",\"1、学前端_2、js_ts_es6 进阶.md\":\"6d07ba10\",\"1、学前端_3、vue_vue3_vue3进阶.md\":\"7ac622b4\",\"5、运维_jenkins.md\":\"929081f8\",\"1、学前端_2、js_ts_typescript.md\":\"875a4aa4\",\"2、数据库_mysql_mysql核心_设计.md\":\"7faf46d1\",\"2、数据库_mysql_mysql核心_基础.md\":\"d8e97f3e\",\"1、学前端_1、html_css_html基础.md\":\"7584d076\",\"1、学前端_5、专题篇_问题篇.md\":\"e893aaa2\",\"2、数据库_mysql_mysql面试_进阶.md\":\"f934806d\",\"3、springboot_运维_原理.md\":\"f4a39db6\",\"2、数据库_influxdb.md\":\"6e1711e1\",\"3、springboot_新特性.md\":\"cdf3e307\",\"mybatis_mybatisplus_jpa.md\":\"8e41681b\",\"1、学前端_5、小程序_小程序优化.md\":\"a2185198\",\"2、数据库_redis_redis基础.md\":\"856df0e0\",\"linux_实用脚本.md\":\"f2299dd5\",\"4、微服务_必备_分布式基础.md\":\"d49863d5\",\"2、数据库_redis_redis优化.md\":\"e66ae32f\",\"4、微服务_springsecurity_进阶篇.md\":\"235a8e9e\",\"5、运维_chatgpt.md\":\"10db3823\",\"2、数据库_mysql_分库分表.md\":\"e1c8a095\",\"start.md\":\"9bc1ff8d\",\"5、运维_github.md\":\"2ec6c735\",\"java学前端_css.md\":\"f11b47f0\",\"1、学前端_5、专题篇_知识篇.md\":\"a463ed8d\",\"linux_软件部署.md\":\"d6722925\",\"2、数据库_neo4j.md\":\"97ad22ac\",\"team.md\":\"ce467a6a\",\"nginx_实战篇.md\":\"7785486e\",\"index.md\":\"8c3ec167\",\"计算机基础_计算机网络_网络基础.md\":\"7a54a85d\",\"1、学前端_4、node_进阶篇.md\":\"60f6db69\",\"java_java集合.md\":\"a049b313\",\"1、学前端_3、vue_vue3_vue3高级.md\":\"614d1516\",\"1、学前端_5、小程序_微信小程序.md\":\"9a4be771\",\"5、运维_netty.md\":\"12ca0278\",\"2、数据库_mysql_mysql核心_运维.md\":\"83f97c16\",\"idea_vs code.md\":\"afdcb593\",\"java学前端_vue3_组件.md\":\"1086884e\",\"idea_chrome.md\":\"4a32afbc\",\"云原生_k8s.md\":\"db58e65a\",\"2、数据库_mysql_mysql核心_进阶.md\":\"61d16dff\",\"ssm_springbatch.md\":\"f799ab4a\",\"三高_分布式.md\":\"db1b8a1b\",\"2、数据库_elasticsearch_1、es基础.md\":\"04d17448\",\"linux_linux基础.md\":\"4b0bf394\",\"idea_idea插件.md\":\"fa86e45a\",\"可视化 _ 监控_可视化大屏.md\":\"004553bd\",\"2、数据库_mongodb_整合.md\":\"3c47d7f4\",\"4、微服务_springsecurity_基础篇.md\":\"534a3401\",\"4、微服务_进阶.md\":\"69095c58\",\"计算机基础_计算机基础_操作系统.md\":\"0f75d113\",\"可视化 _ 监控_zabbix.md\":\"71f2270e\",\"nginx_基础篇.md\":\"c7d8bb50\",\"1、学前端_4、node_项目实战.md\":\"bc5065b8\",\"2、数据库_redis_redis原理.md\":\"5cedf685\",\"可视化 _ 监控_监控基础.md\":\"ac56ce4d\",\"三高_高并发.md\":\"ea9ffc99\",\"2、数据库_redis_redis高级.md\":\"1d5872f6\",\"1、学前端_4、node_基础篇.md\":\"581cc13a\",\"2、数据库_mongodb_基础.md\":\"fb7a0a29\",\"idea_idea基础.md\":\"6f2f9638\",\"4、微服务_必备_sentinel.md\":\"2edfbf6c\",\"2、数据库_elasticsearch_3、es高级.md\":\"ef146606\",\"1、学前端_3、vue_vue3_vue3新语法.md\":\"8afd5409\",\"消息中间件_canal.md\":\"3949163c\",\"ssm_maven.md\":\"2c5e12ed\",\"4、微服务_springsecurity_高级篇.md\":\"882d3ff3\",\"linux_linux进阶.md\":\"188ef7b4\",\"计算机基础_设计模式_uml.md\":\"634ba256\",\"计算机基础_算法_leetcode.md\":\"77162fb9\",\"项目实战_小兔鲜_进阶篇1.md\":\"17c52c81\",\"1、学前端_2、js_ts_es6 基础.md\":\"fda3f18b\",\"项目实战_小兔鲜_进阶篇2.md\":\"a0f23006\",\"软件测试_测试基础.md\":\"8c1060cd\",\"2、数据库_redis_本地缓存.md\":\"00617fe6\",\"nginx_面试篇.md\":\"e3fb373a\",\"mybatis_mybatisplus_mybatis.md\":\"9239e0ad\",\"linux_shell.md\":\"ae53d83b\",\"2、数据库_mysql_mysql核心_优化.md\":\"36230425\",\"项目实战_项目推荐.md\":\"f9d97630\",\"mybatis_mybatisplus_mybatisplus.md\":\"0030fd35\",\"项目实战_百度地图_进阶篇.md\":\"c8b93267\",\"三高_高可用.md\":\"323840c5\",\"java_java新特性.md\":\"22abf56d\",\"软件测试_压力测试.md\":\"9ab44440\",\"java学前端_html_js.md\":\"e0fcd240\",\"2、数据库_redis_redis实战.md\":\"d6daeeab\",\"nginx_进阶篇.md\":\"e6b63195\",\"三高_秒杀.md\":\"3878bb64\",\"5、运维_git.md\":\"0264925c\",\"java_java进阶.md\":\"e79cb5b4\",\"并发 _ 多线程_基础篇.md\":\"7adbfac5\",\"项目实战_百度地图_基础篇.md\":\"8afa5954\",\"java学前端_react.md\":\"3ec827dd\",\"1、学前端_1、html_css_css基础.md\":\"01b56712\",\"项目实战_小兔鲜_基础篇.md\":\"646f5df5\",\"1、学前端_2、js_ts_js 基础.md\":\"cb13e36f\",\"可视化 _ 监控_监控进阶.md\":\"0cdbc292\",\"计算机基础_设计模式_基础篇.md\":\"51617287\",\"计算机基础_数据结构_基础篇.md\":\"b2bfd8d4\",\"项目实战_苍穹外卖_进阶篇.md\":\"48415e41\",\"ssm_spring.md\":\"ab514659\",\"消息中间件_rabbitmq.md\":\"45b1eb28\",\"1、学前端_1、html_css_网页进阶.md\":\"db998248\",\"消息中间件_kafka.md\":\"b747dabf\",\"云原生_docker.md\":\"983c7ba7\",\"4、微服务_必备_分布式锁.md\":\"5af1cf8d\",\"消息中间件_rocketmq.md\":\"d441da85\",\"项目实战_黑马头条_基础篇.md\":\"b05af3a6\",\"ssm_springmvc.md\":\"81b9714f\",\"项目实战_支付.md\":\"1d7407dd\",\"项目实战_黑马头条_进阶篇2.md\":\"bff0015b\",\"项目实战_黑马头条_进阶篇.md\":\"19f18388\",\"java学前端_vue2_组件.md\":\"58c6b1df\",\"3、springboot_基础篇.md\":\"529c66f4\",\"3、springboot_应用篇.md\":\"8b92aa61\",\"项目实战_黑马头条_高级篇.md\":\"227c08c1\",\"1、学前端_5、小程序_uniapp.md\":\"71a282b4\",\"项目实战_云尚办公_基础篇.md\":\"1fe188ba\",\"并发 _ 多线程_并发完善.md\":\"26619c46\",\"1、学前端_2、js_ts_js 进阶.md\":\"657dfb8f\",\"java_java高级.md\":\"23782d1a\",\"java_java基础.md\":\"86d67c77\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"VitePress\",\"description\":\"A VitePress site\",\"base\":\"/notebook/\",\"head\":[],\"appearance\":true,\"themeConfig\":{\"algolia\":{\"appId\":\"DW7O63I9IR\",\"apiKey\":\"f8ed758cdb288a8b06542bc35923c1a1\",\"indexName\":\"notebook\"},\"sidebar\":[{\"text\":\"Java\",\"collapsed\":true,\"items\":[{\"text\":\"Java基础\",\"link\":\"/Java/Java基础\"},{\"text\":\"Java新特性\",\"link\":\"/Java/Java新特性\"},{\"text\":\"Java进阶\",\"link\":\"/Java/Java进阶\"},{\"text\":\"Java集合\",\"link\":\"/Java/Java集合\"},{\"text\":\"Java高级\",\"link\":\"/Java/Java高级\"}]},{\"text\":\"Linux\",\"collapsed\":true,\"items\":[{\"text\":\"Linux基础\",\"link\":\"/Linux/Linux基础\"},{\"text\":\"Linux新特性\",\"link\":\"/Linux/Linux进阶\"},{\"text\":\"Shell脚本\",\"link\":\"/Linux/Shell\"},{\"text\":\"实用脚本\",\"link\":\"/Linux/实用脚本\"},{\"text\":\"软件部署\",\"link\":\"/Linux/软件部署\"}]},{\"text\":\"Nginx\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/Nginx/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/Nginx/进阶篇\"},{\"text\":\"实战篇\",\"link\":\"/Nginx/实战篇\"},{\"text\":\"面试篇\",\"link\":\"/Nginx/面试篇\"}]},{\"text\":\"SSM\",\"collapsed\":true,\"items\":[{\"text\":\"Maven\",\"link\":\"/SSM/Maven\"},{\"text\":\"Spring\",\"link\":\"/SSM/Spring\"},{\"text\":\"SpringMVC\",\"link\":\"/SSM/SpringMVC\"},{\"text\":\"SpringBatch\",\"link\":\"/SSM/SpringBatch\"}]},{\"text\":\"SpringBoot\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/3、SpringBoot/基础篇\"},{\"text\":\"应用篇\",\"link\":\"/3、SpringBoot/应用篇\"},{\"text\":\"新特性\",\"link\":\"/3、SpringBoot/新特性\"},{\"text\":\"运维&原理\",\"link\":\"/3、SpringBoot/运维&原理\"}]},{\"text\":\"SpringCloud\",\"collapsed\":true,\"items\":[{\"text\":\"SpringCloud\",\"link\":\"/4、微服务/进阶\"},{\"text\":\"Sentinel\",\"link\":\"/4、微服务/必备/Sentinel\"}]},{\"text\":\"SpringSecurity\",\"collapsed\":true,\"items\":[{\"text\":\"SpringSecurity基础篇\",\"link\":\"/4、微服务/SpringSecurity/基础篇\"},{\"text\":\"SpringSecurity进阶篇\",\"link\":\"/4、微服务/SpringSecurity/进阶篇\"},{\"text\":\"SpringSecurity高级篇\",\"link\":\"/4、微服务/SpringSecurity/高级篇\"}]},{\"text\":\"Mybatis & MybatisPlus\",\"collapsed\":true,\"items\":[{\"text\":\"Mybatis\",\"link\":\"/Mybatis&MybatisPlus/Mybatis\"},{\"text\":\"MybatisPlus\",\"link\":\"/Mybatis&MybatisPlus/MybatisPlus\"},{\"text\":\"JPA\",\"link\":\"/Mybatis&MybatisPlus/JPA\"}]},{\"text\":\"Git & ChatGPT\",\"collapsed\":true,\"items\":[{\"text\":\"Git\",\"link\":\"/5、运维/Git\"},{\"text\":\"Github\",\"link\":\"/5、运维/Github\"},{\"text\":\"ChatGPT\",\"link\":\"/5、运维/ChatGPT\"},{\"text\":\"Jenkins\",\"link\":\"/5、运维/Jenkins\"},{\"text\":\"Netty\",\"link\":\"/5、运维/Netty\"}]},{\"text\":\"数据库\",\"collapsed\":true,\"items\":[{\"text\":\"MySQL\",\"collapsed\":true,\"items\":[{\"text\":\"MySQL基础\",\"link\":\"/2、数据库/MySQL/MySQL核心/基础\"},{\"text\":\"MySQL进阶\",\"link\":\"/2、数据库/MySQL/MySQL核心/进阶\"},{\"text\":\"MySQL优化\",\"link\":\"/2、数据库/MySQL/MySQL核心/优化\"},{\"text\":\"MySQL设计\",\"link\":\"/2、数据库/MySQL/MySQL核心/设计\"},{\"text\":\"MySQL运维\",\"link\":\"/2、数据库/MySQL/MySQL核心/运维\"},{\"text\":\"分库分表\",\"link\":\"/2、数据库/MySQL/分库分表\"}]},{\"text\":\"Redis\",\"collapsed\":true,\"items\":[{\"text\":\"Redis基础\",\"link\":\"/2、数据库/Redis/Redis基础\"},{\"text\":\"Redis优化\",\"link\":\"/2、数据库/Redis/Redis优化\"},{\"text\":\"Redis原理\",\"link\":\"/2、数据库/Redis/Redis原理\"},{\"text\":\"Redis高级\",\"link\":\"/2、数据库/Redis/Redis高级\"},{\"text\":\"Redis实战\",\"link\":\"/2、数据库/Redis/Redis实战\"},{\"text\":\"本地缓存\",\"link\":\"/2、数据库/Redis/本地缓存\"}]},{\"text\":\"MongoDB\",\"collapsed\":true,\"items\":[{\"text\":\"MongoDB基础\",\"link\":\"/2、数据库/MongoDB/基础\"},{\"text\":\"MongoDB进阶\",\"link\":\"/2、数据库/MongoDB/整合\"}]},{\"text\":\"ElasticSearch\",\"collapsed\":true,\"items\":[{\"text\":\"ES基础\",\"link\":\"/2、数据库/ElasticSearch/1、ES基础\"},{\"text\":\"ES高级\",\"link\":\"/2、数据库/ElasticSearch/3、ES高级\"}]},{\"text\":\"InfluxDB\",\"link\":\"/2、数据库/influxdb\"},{\"text\":\"Neo4j\",\"link\":\"/2、数据库/Neo4j\"}]},{\"text\":\"高并发 & 秒杀 & 分布式\",\"collapsed\":true,\"items\":[{\"text\":\"分布式理论\",\"link\":\"/三高/分布式\"},{\"text\":\"分布式锁\",\"link\":\"/4、微服务/必备/分布式锁\"},{\"text\":\"秒杀\",\"link\":\"/三高/秒杀\"},{\"text\":\"高可用\",\"link\":\"/三高/高可用\"},{\"text\":\"高并发\",\"link\":\"/三高/高并发\"}]},{\"text\":\"云原生\",\"collapsed\":true,\"items\":[{\"text\":\"Docker\",\"link\":\"/云原生/Docker\"},{\"text\":\"K8S\",\"link\":\"/云原生/K8S\"}]},{\"text\":\"可视化 & 监控\",\"collapsed\":true,\"items\":[{\"text\":\"监控基础\",\"link\":\"/可视化 & 监控/监控基础\"},{\"text\":\"监控进阶\",\"link\":\"/可视化 & 监控/监控进阶\"},{\"text\":\"可视化大屏\",\"link\":\"/可视化 & 监控/可视化大屏\"},{\"text\":\"Zabbix\",\"link\":\"/可视化 & 监控/Zabbix\"}]},{\"text\":\"学前端\",\"collapsed\":true,\"items\":[{\"text\":\"HTML+CSS\",\"collapsed\":true,\"items\":[{\"text\":\"HTML基础\",\"link\":\"/1、学前端/1、HTML+CSS/HTML基础\"},{\"text\":\"CSS基础\",\"link\":\"/1、学前端/1、HTML+CSS/CSS基础\"},{\"text\":\"网页进阶\",\"link\":\"/1、学前端/1、HTML+CSS/网页进阶\"}]},{\"text\":\"JS+TS\",\"collapsed\":true,\"items\":[{\"text\":\"JS基础\",\"link\":\"/1、学前端/2、JS+TS/JS 基础\"},{\"text\":\"JS进阶\",\"link\":\"/1、学前端/2、JS+TS/JS 进阶\"},{\"text\":\"ES6基础\",\"link\":\"/1、学前端/2、JS+TS/ES6 基础\"},{\"text\":\"ES6进阶\",\"link\":\"/1、学前端/2、JS+TS/ES6 进阶\"},{\"text\":\"TS基础\",\"link\":\"/1、学前端/2、JS+TS/TypeScript\"}]},{\"text\":\"NodeJS\",\"collapsed\":true,\"items\":[{\"text\":\"Node基础\",\"link\":\"/1、学前端/4、Node/基础篇\"},{\"text\":\"Node进阶\",\"link\":\"/1、学前端/4、Node/进阶篇\"},{\"text\":\"项目实战\",\"link\":\"/1、学前端/4、Node/项目实战\"}]},{\"text\":\"Vue\",\"collapsed\":true,\"items\":[{\"text\":\"Vue3进阶\",\"link\":\"/1、学前端/3、Vue/Vue3/Vue3进阶\"},{\"text\":\"Vue3高级\",\"link\":\"/1、学前端/3、Vue/Vue3/Vue3高级\"},{\"text\":\"Vue3新语法\",\"link\":\"/1、学前端/3、Vue/Vue3/Vue3新语法\"},{\"text\":\"项目实战\",\"link\":\"/1、学前端/3、Vue/Vue2/Vue2项目\"}]},{\"text\":\"小程序\",\"collapsed\":true,\"items\":[{\"text\":\"小程序基础\",\"link\":\"/1、学前端/5、小程序/微信小程序\"},{\"text\":\"小程序优化\",\"link\":\"/1、学前端/5、小程序/小程序优化\"},{\"text\":\"uniapp\",\"link\":\"/1、学前端/5、小程序/uniapp\"},{\"text\":\"项目实战\",\"link\":\"/1、学前端/5、小程序/小程序项目\"}]}]},{\"text\":\"计算机基础\",\"collapsed\":true,\"items\":[{\"text\":\"数据结构\",\"link\":\"/计算机基础/数据结构/基础篇\"},{\"text\":\"操作系统\",\"link\":\"/计算机基础/计算机基础/操作系统\"},{\"text\":\"设计模式\",\"link\":\"/计算机基础/设计模式/基础篇\"},{\"text\":\"计算机网络\",\"link\":\"/计算机基础/计算机网络/网络基础\"},{\"text\":\"UML\",\"link\":\"/计算机基础/设计模式/UML\"},{\"text\":\"LeetCode\",\"link\":\"/计算机基础/算法/LeetCode\"}]},{\"text\":\"项目实战\",\"collapsed\":true,\"items\":[{\"text\":\"云尚办公\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/云尚办公/基础篇\"}]},{\"text\":\"小兔鲜\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/小兔鲜/基础篇\"},{\"text\":\"进阶篇1\",\"link\":\"/项目实战/小兔鲜/进阶篇1\"},{\"text\":\"进阶篇2\",\"link\":\"/项目实战/小兔鲜/进阶篇2\"}]},{\"text\":\"地图\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/百度地图/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/项目实战/百度地图/进阶篇\"}]},{\"text\":\"苍穹外卖\",\"collapsed\":true,\"items\":[{\"text\":\"进阶篇\",\"link\":\"/项目实战/苍穹外卖/进阶篇\"}]},{\"text\":\"黑马头条\",\"collapsed\":true,\"items\":[{\"text\":\"基础篇\",\"link\":\"/项目实战/黑马头条/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/项目实战/黑马头条/进阶篇\"},{\"text\":\"进阶篇2\",\"link\":\"/项目实战/黑马头条/进阶篇2\"},{\"text\":\"高级篇\",\"link\":\"/项目实战/黑马头条/高级篇\"}]},{\"text\":\"支付\",\"link\":\"/项目实战/支付\"},{\"text\":\"项目推荐\",\"link\":\"/项目实战/项目推荐\"}]},{\"text\":\"团队成员\",\"link\":\"/team\"}],\"siteTitle\":\"任硕的文档\",\"logo\":\"/Vue.png\",\"nav\":[{\"text\":\"Java学前端\",\"items\":[{\"items\":[{\"text\":\"HTML+JS\",\"link\":\"/Java学前端/HTML+JS\"},{\"text\":\"CSS\",\"link\":\"/Java学前端/CSS\"},{\"text\":\"Vue2+组件\",\"link\":\"/Java学前端/Vue2+组件\"},{\"text\":\"Vue3+组件\",\"link\":\"/Java学前端/Vue3+组件\"},{\"text\":\"React\",\"link\":\"/Java学前端/React\"}]}],\"activeMatch\":\"/Java/\"},{\"text\":\"软件测试\",\"items\":[{\"items\":[{\"text\":\"测试基础\",\"link\":\"/软件测试/测试基础\"},{\"text\":\"压力测试\",\"link\":\"/软件测试/压力测试\"}]}]},{\"text\":\"多线程\",\"items\":[{\"items\":[{\"text\":\"基础篇\",\"link\":\"/并发 & 多线程/基础篇\"},{\"text\":\"进阶篇\",\"link\":\"/并发 & 多线程/并发完善\"}]}]},{\"text\":\"开发工具\",\"items\":[{\"items\":[{\"text\":\"Chrome\",\"link\":\"/IDEA/Chrome\"},{\"text\":\"IDEA基础\",\"link\":\"/IDEA/IDEA基础\"},{\"text\":\"IDEA插件\",\"link\":\"/IDEA/IDEA插件\"},{\"text\":\"VS Code\",\"link\":\"/IDEA/VS Code\"}]}]},{\"text\":\"消息中间件\",\"items\":[{\"items\":[{\"text\":\"RabbitMQ\",\"link\":\"/消息中间件/RabbitMQ\"},{\"text\":\"RocketMQ\",\"link\":\"/消息中间件/RocketMQ\"},{\"text\":\"Kafka\",\"link\":\"/消息中间件/Kafka\"},{\"text\":\"Canal\",\"link\":\"/消息中间件/Canal\"}]}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/renshuo123/renshuo123.github.io\"},{\"icon\":\"twitter\",\"link\":\"#\"},{\"icon\":{\"svg\":\"<svg t=\\\"1676028692954\\\" class=\\\"icon\\\" ...</path></svg>\"},\"link\":\"https://github.com/\"}]},\"locales\":{},\"scrollOffset\":90,\"cleanUrls\":false}");</script>
    
  </body>
</html>